[fleXive]™ 3.2 Reference Documentation

Revision 3006, 2014/10/23 15:58

Authors:

Johannes Wernig-Pichler <>
Anton Zrzavy <>

Table of Contents

1. Introduction
2. Installing [fleXive]
Quickstart
Prerequisites
Using [fleXive]
The [fleXive] Installer
Maven support
Further steps
[fleXive] installation guide for JBoss 4.2.2 GA
Required libraries
System configuration
Database Setup
Deployment
Starting [fleXive]
Refining your configuration
[fleXive] installation guide for Glassfish 2 and 3
Required libraries
Running the ant task
Database Setup
Setting up and starting Glassfish
Creating the Connection pools
Deployment
Building [fleXive] from source
Troubleshooting
Multiple JBoss installations
JBossCache, Linux and IPv6
Apache ANT
3. The [fleXive] Tutorial
Your first [fleXive] application
Defining the data model
Retrieving data from the database
Building the graphical user interface
Deployment descriptor and JSF configuration
Compiling and deploying the application
Preview of the administration GUI
Tutorial 1: The Document Store Application
Defining the data model
The document browser and upload pages
Using <fx:resultValue>
Passing content instances to the editor form
The upload form
Retrieving data from the database
Rendering previews: the ThumbnailServlet
Wiring up the faces configuration
Compiling and deploying the application
4. Writing [fleXive] applications
The [fleXive] distribution
Getting started
IDE integration
Eclipse
IntelliJ IDEA
Programming [fleXive]
Package for distribution
Build file support
Build targets
Building applications with Apache Maven
The [fleXive] Maven repository
Project archetypes
The EAR archetype
The WAR archetype
[fleXive] artifacts
5. The big picture
6. The [fleXive] core components
User Management
Creating a user
Updating a user
Removing a user
Security
Overview
Authentication / FxContext / UserTickets
Divisions
Mandators
ACLs - Access Control Lists
Role Based Security
Structure Engine
Overview
Types
Root Type
Type parameters
Creating a new type
Data types
Handling binary content
Select lists
Select list parameters
Select list item parameters
Select list creation and persistance
Select list item permissions
Multiplicity
Properties and property assignments
Conceptual explanation of property assignments
Property parameters
Uniqueness of values
Groups and group assignments
Group parameters
Group modes
Property and group options
The GroovyTypeBuilder
GroovyTypeBuilder Syntax
GroovyTypeBuilder Attributes
Property and Group Creation - Default Values
GroovyTypeBuilder Usage Examples
The GroovyOptionBuilder
Content Engine
Overview
Using FxContent
Primary keys and versions
Search Engine
An introduction to FxSQL
Select user-defined columns
Select all columns
Fulltext search
Properties versus assignments
Content permissions
Metadata
Lock information
Tree Search
Briefcase Search
Date and Time Functions
Type conditions
Select list properties
Resolving system properties
Filters
Order by
Comments
Operator table
Literal value formatting
SqlQueryBuilder: Building queries in Java
Conditions
Filters
Nested conditions
Search parameters
The Groovy query builder
Working with search results
Accessing result rows
Projections
Briefcase Engine
Tree Engine
Security
Scripting
Edit and Live modes
Examples
Scripting Engine
Overview
Events and Bindings
Run-Once and Startup Scripts
Workflow Engine
Conceptual Overview
Creating and updating workflows
Configuration Engine
Conceptual Overview
Working with configuration parameters
Object parameters
7. JSF Support
The [fleXive] component library
Using the component library
Content manipulation
<fx:content>
<fx:children>
<fx:contentEditor>
<fx:value>
<fx:fxValueInput>
<fx:fxValueInputLanguageSelect>
<fx:resultValue>
<fx:contentList>
<fx:thumbnail>
<fx:resultTable>
<fx:resultTableUpdater>
<fx:colorPicker>
<fx:linearizeFxValueInputs>
Navigation components
<fx:navigation>
Framework components
<fx:includes>
<fx:yuiSetup>
Form utility components
<fx:formRow>
<fx:fieldSet>
JSF managed beans
fxContentViewBean
fxAuthenticationBean
fxMessageBean
Delivering XHTML pages from the classpath
URI rewriting for contents
Writing reusable [fleXive] components
JSF components
Build file support
Deployment
Packaging
EJB components
The JSF plugin API
Overview
The plugin registry
Registering plugins at extension points
8. JBoss Seam and [fleXive]
Integration guide
Programming [fleXive] in Seam
Authorization and Authentication
9. The [fleXive] administration GUI
[fleXive] administration GUI basics
Running the administration GUI
Logging in
[fleXive] administration GUI display areas
administration GUI overview
User preferences
The structure editor
The structure editor GUI
Structure tree
Structure tree interaction
View mode vs. edit mode
The type editor
The group editor
The property editor
The options editor
The scripting editor
Saving and deleting
Structure export and import
Structure export GUI
Structure import GUI
The instance editor
Creating contents
Content creation UI elements
Editing contents
Content versioning
Content import and export
Content locking
The content tree
Live and edit tree
The content tree's context menu
The visual query editor
Creating queries with the visual query editor
Query parameters
Query results
Saved queries
Briefcases
[fleXive] administration
User account administration
Creating a user
The user account overview
User group administration
The user group overview
Mandator administration
Creating a mandator
The mandator overview
ACL administration
Creating an ACL
The permission matrix
The ACL overview
Workflow administration
Step definitions
Step definition overview
Creating a workflow
Editing a workflow
The workflow overview
Script administration
Creating a script
The script overview
Run-Once Script Information
The Script Console
Select list administration
Creating a select list
Editing a select list and its select items
Select list overview
Export and Import of Structures and Division
System administration
Search settings
CMIS SQL query
System Configuration
System parameters
Lock administration
History viewer
System information
Language settings
Create random test data for content instances
10. [fleXive] Plugins
Core JSF components
Global Configuration Plugin
Usage
Administration GUI Feedback Plugin
Glossary
Bibliography

List of Figures

3.1. The document store application, version 1

List of Tables

6.1. Account Attributes
6.2. [fleXive] structure elements
6.3. FxTypeEdit parameters
6.4. [fleXive] data types
6.5. FxSelectList parameters
6.6. FxSelectListItem parameters
6.7. Relevant permissions for working with select list items
6.8. FxPropertyEdit/FxPropertyAssignmentEdit shared parameters
6.9. FxPropertyEdit exclusive parameters
6.10. FxPropertyAssignmentEdit exclusive parameters
6.11. FxGroupEdit/FxGroupAssignmentEdit shared parameters
6.12. FxGroupEdit exclusive parameters
6.13. FxGroupAssignmentEdit exclusive parameters
6.14. Supported options
6.15. Type Attributes
6.16. Properties and Groups - Common Attributes (Property / Group Creation)
6.17. Exclusive property attributes (property creation)
6.18. Exclusive group attributes (group creation)
6.19. Property and Group Assignments - common attributes (property / group assignments and changes thereof)
6.20. Exclusive property assignment attributes (assignments and changes thereof)
6.21. Exclusive group assignment attributes (assignments and changes thereof)
6.22. XPath to value mapping
6.23. Date and Time functions provided in FxSQL
6.24. Type conditions in FxSQL
6.25. System property fields provided in FxSQL
6.26. FxSQL operator table
6.27. FxSQL literal value formats
9.1. FxScriptEvent list

List of Examples

6.1. Creating a new user
6.2. Updating a user
6.3. Removing a user
6.4. Creating a new FxType
6.5. Creating a new FxType using the GroovyTypeBuilder
6.6. Handling binary content
6.7. Creating new properties
6.8. Reusing property assignments
6.9. Property assignment concepts
6.10. Creating a new group
6.11. Creating a new group using the GroovyTypeBuilder
6.12. Creating a simple type with one attribute
6.13. Creating a "Person" type in Java
6.14. Creating a "Person" type in Groovy
6.15. Creating "Person" instances
6.16. Creating a nested query
6.17. Specifying a query with the GroovyQueryBuilder
6.18. Iterating over a FxResultSet
6.19. Directly accessing the search results using getRows()
6.20. Working with briefcases and metadata
6.21. Creating a new tree node attached to the root node
6.22. Clear both Live and Edit Tree in the groovy console
6.23. Creating a workflow step definition
6.24. Creating a new workflow with steps and routes
7.1. Render FxContent property values
7.2. Edit an existing FxContent instance
7.3. Create a new FxContent instance
7.4. Add values to properties with multiplicities
7.5. Add a new empty group to a content instance
7.6. Use InputMappers for application-specific properties
7.7. Iterating over property values
7.8. Iterating over group values and nested iterators
7.9. Content Editor simple usage
7.10. Content Editor adding buttons
7.11. Content Editor calling the backing bean
7.12. An input row for editing a FxString value
7.13. An autocomplete handler for user names
7.14. Writing custom autocomplete handlers
7.15. Using fx:resultTable to submit and render FxSQL queries
7.16. Updating a resultTable through an Ajax4jsf update
7.17. Using the color picker with a JSF input component
7.18. Using the color picker for a form input
7.19. A JSF/HTML navigation that selects the clicked node
7.20. Rendering a plain text input row
7.21. A fieldset with two formrow elements
7.22. Making a [fleXive] application extensible
7.23. Writing a plugin for a [fleXive] application
7.24. Specifying a PluginFactory in flexive-plugins-config.xml

Managing and operating on hierarchical data structures is an issue in many projects, alongside with security concerns, versioning and querying. Till now, neither open nor closed source projects or frameworks offer possibilities to dynamically define and manipulate the structure of hierarchical data in a scope like [fleXive]. Hence the urge to provide a solution that combines flexibility, security and performance in one customizable package.

[fleXive] is a JavaEE 5 open-source (LGPL) framework for the development of complex and evolving (web-)applications. It speeds up development as it handles numerous important application issues and keeps your application flexible over the development-cycle. It is based on the lastest industry-standards like EJB 3, JSF, etc.

The [fleXive] administration GUI is an optional application built on top of the framework and is licensed under the GNU General Public License (GPL). It helps you to visually manage most aspects of [fleXive] - like defining data structures, building queries, manage users and security, etc.

[fleXive] concentrates on enterprise-scale content storage and retrieval, and includes comprehensive JSF support for displaying and manipulating these contents in web applications. The runtime environment can be included in existing JavaEE applications, but you can also build new applications and package them into stand-alone JavaEE applications. Our emphasis lies on the runtime environment, so if you are looking e.g. for tool-driven JSF development, take a look at the popular JBoss Seam framework and embed the [fleXive] runtime environment in that project.

The goal of [fleXive] is to relieve you from many tendious and repetitive programming tasks when building secure, data-centric (web-)applications.

If you are new to [fleXive] please use the following tools to get started:

  1. The [fleXive] website will provide you with top-level information about [fleXive]. Have a look at the "Explore [fleXive]" section.

  2. This reference documentation will provide all information to use [fleXive] for the development of your application.

  3. In the [fleXive] wiki you will find more tutorials, HowTo's, FAQs, samples and developer infos

For questions, use the [fleXive] forum and mailinglists. We also provide a issue trackings system for bug reports and feature requests. In case you are interested in the development of [fleXive], have a look at the Development Section on the [fleXive] website and join the developer mailinglist.

[fleXive] is a Professional Open Source project. UCS - unique computing solutions gmbh (http://www.ucs.at) offers commercial development support and training for [fleXive].

  1. The Sun Java 6 Platform, Standard Edition Development Kit

    1. Set your JAVA_HOME and JDK_HOME environment variable to your JDK root directory (e.g. JAVA_HOME=/usr/lib/jvm/java-6-sun on Linux, C:\Java\jdk1.6.0_15 on Windows)
  2. If you want to create your own projects: a build tool, either Apache Ant or Apache Maven.

The easiest way to get started is to download the Java-based installer. It provides a preconfigured [fleXive] instance that can be accessed with the administration GUI. WebDAV and CMIS connectors are also included.

The common options for custom development and deployment are:

  • Create a new EAR or WAR project with a Maven archetype. EAR projects default to JavaEE 5, while standalone WAR deployments (not part of an EAR) require a JavaEE 6 container.

  • To use [fleXive] in an existing project, include the flexive-ejb artifact.

  • To use the administration GUI in a JavaEE 6 application server (web or full profile), download and deploy flexive-admin.war from the download page.

  • Run the Java-based installer from our download page. If your browser does not give you the option to run it directly, double-click on the downloaded flexive-installer.jar file or launch it from the command line with java -jar flexive-installer.jar.

    In a headless environment (a server without a graphics environment), you cannot use the GUI installer. Launch the console version with java -jar flexive-installer.jar -console instead.

  • Start the [fleXive] server using the created shortcut, or manually using the start.sh (for Unix, Mac) or start.bat (Windows) scripts in the installation directory.

  • Open the [fleXive] administration GUI using the shortcut from the desktop or the program group or point your browser to http://localhost:8080/flexive/adm/ and login with the default credentials (supervisor/supervisor).

Now that you got [fleXive] up and running, you might...

  • watch the screencasts on our homepage giving you an introduction to the administration GUI and to the process of writing applications with [fleXive],

  • walk through the tutorials if you want to create applications using [fleXive],

  • or read more about writing applications with [fleXive]

If you had troubles installing [fleXive], please go to the troubleshooting section or ask in the forums.

  • Create and copy the datasource config file flexive-ds.xml to ${jboss.home}/server/default/deploy/ and adapt to your datasource(s) or simply copy it from the [fleXive] source tree, if available. For the H2 database, we provide the flexive-h2-ds.xml configuration file as a template. In the flexive-[h2-]ds.xml configure your database connections as follows if using MySQL: For the flexive database

    <!-- transactional datasource -->
    <xa-datasource>
        <jndi-name>jdbc/flexiveDivision1</jndi-name>
        <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
        <!-- Note: "&amp;" has to be used instead of "&" for parameters -->
        <xa-datasource-property name="URL">jdbc:mysql://localhost:3306/flexive?useUnicode=true&characterEncoding=utf8&characterResultSets=utf8</xa-datasource-property>
        <user-name>root</user-name>
        <password>a</password>
    </xa-datasource>
    
    <!-- non-transactional datasource for database structure patches, Quartz, etc. -->
    <no-tx-datasource>
        <jndi-name>jdbc/flexiveDivision1NoTX</jndi-name>
        <driver-class>com.mysql.jdbc.Driver</driver-class>
        <!-- Note: "&amp;" has to be used instead of "&" for parameters -->
        <connection-url>jdbc:mysql://localhost:3306/flexive?useUnicode=true&characterEncoding=utf8&characterResultSets=utf8</connection-url>
        <user-name>root</user-name>
        <password>a</password>
    </no-tx-datasource>
    
    

    For the flexiveConfiguration database

    <xa-datasource>
        <jndi-name>jdbc/flexiveConfiguration</jndi-name>
        <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
        <!-- Note: "&amp;" has to be used instead of "&" for parameters -->
        <xa-datasource-property name="URL">jdbc:mysql://localhost:3306/flexiveConfiguration?useUnicode=true&characterEncoding=utf8&characterResultSets=utf8</xa-datasource-property>
        <user-name>root</user-name>
        <password>a</password>
    </xa-datasource>
    

    flexive-ds.xml

                                    
    <!--
     JBoss MySQL Datasource Configuration
    
     Please make sure mysql-connector-java-<version>-bin.jar is copied to the <JBoss>/server/<profile>/lib directory!
    
     See http://www.redhat.com/docs/manuals/jboss/jboss-eap-4.3/doc/Server_Configuration_Guide/html/Connectors_on_JBoss-Configuring_JDBC_DataSources.html
     for detailed configuration infos
    -->
    <datasources>
    
        <!--
            transactional datasource, as configured in the global configuration
        -->
        <xa-datasource>
            <jndi-name>jdbc/flexiveDivision1</jndi-name>
            <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
            <!-- Note: "&amp;" has to be used instead of "&" for parameters -->
            <xa-datasource-property name="URL">jdbc:mysql://localhost:3306/flexive?useUnicode=true&amp;characterEncoding=utf8&amp;characterResultSets=utf8</xa-datasource-property>
            <user-name>root</user-name>
            <password>a</password>
    
            <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
            <no-tx-separate-pools/>
            <!-- This disables transaction interleaving (which BTW, most DB vendors don't support) -->
            <track-connection-by-tx/>
            <isSameRM-override-value>false</isSameRM-override-value>
    
            <!--pooling parameters-->
            <min-pool-size>5</min-pool-size>
            <max-pool-size>20</max-pool-size>
            <blocking-timeout-millis>5000</blocking-timeout-millis>
            <idle-timeout-minutes>15</idle-timeout-minutes>
    
            <!-- If you supply the usr/pw from a JAAS login module -->
            <security-domain/>
    
            <exception-sorter-class-name>
                com.mysql.jdbc.integration.jboss.ExtendedMysqlExceptionSorter
            </exception-sorter-class-name>
            <valid-connection-checker-class-name>
                com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker
            </valid-connection-checker-class-name>
            <metadata>
                <type-mapping>mySQL</type-mapping>
            </metadata>
        </xa-datasource>
    
        <!--
            non-transactional datasource for database structure patches, Quartz, etc.
            As per convention, non-transactional datasources have the same name like
            transactional but use the suffix "NoTX"
        -->
        <no-tx-datasource>
            <jndi-name>jdbc/flexiveDivision1NoTX</jndi-name>
            <driver-class>com.mysql.jdbc.Driver</driver-class>
            <!-- Note: "&amp;" has to be used instead of "&" for parameters -->
            <connection-url>jdbc:mysql://localhost:3306/flexive?useUnicode=true&amp;characterEncoding=utf8&amp;characterResultSets=utf8</connection-url>
            <user-name>root</user-name>
            <password>a</password>
    
            <!--pooling parameters-->
            <min-pool-size>5</min-pool-size>
            <max-pool-size>20</max-pool-size>
            <blocking-timeout-millis>5000</blocking-timeout-millis>
            <idle-timeout-minutes>15</idle-timeout-minutes>
    
            <!-- If you supply the usr/pw from a JAAS login module -->
            <security-domain/>
    
            <exception-sorter-class-name>
                com.mysql.jdbc.integration.jboss.ExtendedMysqlExceptionSorter
            </exception-sorter-class-name>
            <valid-connection-checker-class-name>
                com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker
            </valid-connection-checker-class-name>
            <metadata>
                <type-mapping>mySQL</type-mapping>
            </metadata>
        </no-tx-datasource>
    
        <xa-datasource>
            <jndi-name>jdbc/flexiveConfiguration</jndi-name>
            <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
            <!-- Note: "&amp;" has to be used instead of "&" for parameters -->
            <xa-datasource-property name="URL">jdbc:mysql://localhost:3306/flexiveConfiguration?useUnicode=true&amp;characterEncoding=utf8&amp;characterResultSets=utf8</xa-datasource-property>
            <user-name>root</user-name>
            <password>a</password>
    
            <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
            <no-tx-separate-pools/>
            <!-- This disables transaction interleaving (which BTW, most DB vendors don't support) -->
            <track-connection-by-tx/>
            <isSameRM-override-value>false</isSameRM-override-value>
    
            <!--pooling parameters-->
            <min-pool-size>5</min-pool-size>
            <max-pool-size>20</max-pool-size>
            <blocking-timeout-millis>5000</blocking-timeout-millis>
            <idle-timeout-minutes>15</idle-timeout-minutes>
    
            <!-- If you supply the usr/pw from a JAAS login module -->
            <security-domain/>
    
            <exception-sorter-class-name>
                com.mysql.jdbc.integration.jboss.ExtendedMysqlExceptionSorter
            </exception-sorter-class-name>
            <valid-connection-checker-class-name>
                com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker
            </valid-connection-checker-class-name>
            <metadata>
                <type-mapping>mySQL</type-mapping>
            </metadata>
        </xa-datasource>
    
    </datasources> 
    
                                

    If you are using H2 please apply the following settings:

                                    
    <!--
     JBoss H2 Datasource Configuration
    
     Please make sure h2.jar is copied to the <JBoss>/server/<profile>/lib directory!
    
     See http://www.redhat.com/docs/manuals/jboss/jboss-eap-4.3/doc/Server_Configuration_Guide/html/Connectors_on_JBoss-Configuring_JDBC_DataSources.html
     for detailed configuration infos
    -->
    <datasources>
    
        <!--
            transactional datasource, as configured in the global configuration
        -->
        <xa-datasource>
            <jndi-name>jdbc/flexiveDivision1</jndi-name>
            <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
            <!-- Note: "&amp;" has to be used instead of "&" for parameters -->
            <xa-datasource-property name="URL">jdbc:h2:tcp://localhost:9092/~/flexive;SCHEMA=flexive;MVCC=TRUE;LOCK_TIMEOUT=10000</xa-datasource-property>
            <user-name>sa</user-name>
            <!--<password></password>-->
    
            <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
            <no-tx-separate-pools/>
            <!-- This disables transaction interleaving (which BTW, most DB vendors don't support) -->
            <track-connection-by-tx/>
            <isSameRM-override-value>false</isSameRM-override-value>
    
            <!--pooling parameters-->
            <min-pool-size>5</min-pool-size>
            <max-pool-size>20</max-pool-size>
            <blocking-timeout-millis>5000</blocking-timeout-millis>
            <idle-timeout-minutes>15</idle-timeout-minutes>
    
            <!-- If you supply the usr/pw from a JAAS login module -->
            <security-domain/>
        </xa-datasource>
    
        <!--
            non-transactional datasource for database structure patches, Quartz, etc.
            As per convention, non-transactional datasources have the same name like
            transactional but use the suffix "NoTX"
        -->
        <no-tx-datasource>
            <jndi-name>jdbc/flexiveDivision1NoTX</jndi-name>
            <driver-class>org.h2.Driver</driver-class>
            <!-- Note: "&amp;" has to be used instead of "&" for parameters -->
            <connection-url>jdbc:h2:tcp://localhost:9092/~/flexive;SCHEMA=flexive;MVCC=TRUE;LOCK_TIMEOUT=10000</connection-url>
            <user-name>sa</user-name>
            <!--<password></password>-->
    
            <!--pooling parameters-->
            <min-pool-size>5</min-pool-size>
            <max-pool-size>20</max-pool-size>
            <blocking-timeout-millis>5000</blocking-timeout-millis>
            <idle-timeout-minutes>15</idle-timeout-minutes>
    
            <!-- If you supply the usr/pw from a JAAS login module -->
            <security-domain/>
        </no-tx-datasource>
    
        <xa-datasource>
            <jndi-name>jdbc/flexiveConfiguration</jndi-name>
            <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
            <!-- Note: "&amp;" has to be used instead of "&" for parameters -->
            <xa-datasource-property name="URL">jdbc:h2:tcp://localhost:9092/~/flexive;SCHEMA=flexiveConfiguration;MVCC=TRUE</xa-datasource-property>
            <user-name>sa</user-name>
            <!--<password></password>-->
    
            <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
            <no-tx-separate-pools/>
            <!-- This disables transaction interleaving (which BTW, most DB vendors don't support) -->
            <track-connection-by-tx/>
            <isSameRM-override-value>false</isSameRM-override-value>
    
            <!--pooling parameters-->
            <min-pool-size>5</min-pool-size>
            <max-pool-size>20</max-pool-size>
            <blocking-timeout-millis>5000</blocking-timeout-millis>
            <idle-timeout-minutes>15</idle-timeout-minutes>
    
            <!-- If you supply the usr/pw from a JAAS login module -->
            <security-domain/>
        </xa-datasource>
    
    </datasources>
    
    
                                

To set up the databases do the following

  • Adapt the database.properties file located in your flexive-dist directory of your [fleXive] distribution to your needs:

    # This is the database configuration file used by the flexive setup tasks.
    #
    # Enter the settings for your database server connection to be used for development.
    # Please use the database.vendor property at the bottom of this file to select
    # the database vendor to use.
    
    # MySQL database settings
    
    # The server host or IP
    database.MySQL.host=localhost
    # The server port
    database.MySQL.port=3306
    # The user name and password to be used for creating flexive database structures
    database.MySQL.username=root
    database.MySQL.password=a
    # The root schema where the flexive schemas can be dropped and created
    database.MySQL.schema.root=mysql
    database.MySQL.schema.config=mysql
    # JDBC Driver
    database.MySQL.driver=com.mysql.jdbc.Driver
    # JDBC base URL, will be appended by the database name and the url parameters
    database.MySQL.url.base=jdbc:mysql://${database.MySQL.host}:${database.MySQL.port}/
    # Optional URL parameters to append to the JDBC connect string
    database.MySQL.url.parameters=?useUnicode=true&amp;characterEncoding=UTF-8
    
    # H2 database settings
    
    # The server host or IP
    database.H2.host=localhost
    # The server port
    database.H2.port=9092
    # The user name and password to be used for creating flexive database structures
    database.H2.username=sa
    database.H2.password=
    database.H2.schema.root=flexive
    database.H2.schema.config=flexiveConfiguration
    database.H2.driver=org.h2.Driver
    database.H2.url.base=jdbc:h2:tcp://${database.H2.host}:${database.H2.port}/~/
    database.H2.url.parameters=;SCHEMA=@DATABASE@;MVCC=TRUE
    
    
    #select the database vendor to use
    #database.vendor=H2
    database.vendor=MySQL

    database.properties contains settings for all currently supported database vendors of [fleXive]. You can adapt the setting like choosing different ports or locations for the database. The probably most important setting are the lines at the end of the file:

    #select the database vendor to use
    #database.vendor=H2
    database.vendor=MySQL

    Here you can select the database vendor to use by commenting out the appropriate line. In this example MySQL is the database vendor to be used.

  • Change to the flexive-dist directory of your [fleXive] distribution and run ant db.create db.config.create

    You will be prompted to enter a name for your division database (simply hit enter to keep flexive).

    Warning: Existing databases will be deleted.

  • Extend ${jboss.home}/server/default/conf/jboss-log4j.xml to configure logging:

        
    <!-- Limit flexive -->
    <category name="com.flexive">
        <priority value="INFO"/>
    </category>
    
    <!-- Limit ajax4jsf -->
    <category name="org.ajax4jsf">
        <priority value="WARN"/>
    </category>
    

  • Optional if you need JAAS support: Extend ${jboss.home}/server/default/conf/login-config.xml with the following entry:

        
    <application-policy name = "FxLogin">
         <authentication>
           <login-module code="com.flexive.core.security.FxDefaultLogin" flag="required"></login-module>
         </authentication>
    </application-policy>
    

  • Optional step: To use an external service deployer for JBoss Cache, create the cache service file 99_JBossCacheJNDI42-service.xml in ${jboss.home}/server/default/deploy/ or simply copy it from the [fleXive] source tree, if available:

                                <?xml version="1.0" encoding="UTF-8"?>
    <server>
    
       <loader-repository>com.flexive:archive=flexive.ear
       </loader-repository>
    
       <!-- ========================================================== -->
       <!-- Clustered SFSB cache config for use with JBoss Cache 1.4.x --> 
       <!-- ========================================================== -->
       <mbean code="org.jboss.cache.jmx.CacheJmxWrapper"
              name="jboss.cache:service=JNDITreeCache">
          <!--<depends>jboss:service=Naming</depends>-->
          <!--<depends>jboss:service=TransactionManager</depends>-->
    
          <attribute name="TransactionManagerLookupClass">org.jboss.cache.transaction.GenericTransactionManagerLookup</attribute>
            
           <!--
                   Node locking scheme :
                                       PESSIMISTIC (default)
                                       OPTIMISTIC
           -->
          <attribute name="NodeLockingScheme">PESSIMISTIC</attribute>
           
          <attribute name="ClusterName">[fleXive]-Cache-JNDI</attribute>
          
          <!--
                  Node locking level : SERIALIZABLE
                                       REPEATABLE_READ (default)
                                       READ_COMMITTED
                                       READ_UNCOMMITTED
                                       NONE
          -->
          <attribute name="IsolationLevel">READ_COMMITTED</attribute>
    
          <!--     Valid modes are LOCAL
                                   REPL_ASYNC
                                   REPL_SYNC
          -->
          <attribute name="CacheMode">REPL_ASYNC</attribute>
    
    	  <!-- We want to activate/inactivate regions as beans are deployed -->
          <attribute name="UseRegionBasedMarshalling">true</attribute>
          <!-- Must match the value of "useRegionBasedMarshalling" -->
          <attribute name="InactiveOnStartup">true</attribute>
    
    	  <!-- 
    	       JGroups protocol stack config in XML format.
    		   If your CacheMode is set to REPL_SYNC we recommend you comment
               out the FC (flow control) protocol
    			 
               On Windows machines, because of the media sense feature
               being broken with multicast (even after disabling media sense)
               set the UDP.loopback attribute to true
    	  -->     
          <attribute name="ClusterConfig">
             <config>
                <UDP mcast_addr="${flexive.cluster.udpGroup:229.1.2.5}"
                 mcast_port="${jboss.ejb3sfsbpartition.mcast_port:45557}"
                 tos="8"
                 ucast_recv_buf_size="20000000"
                 ucast_send_buf_size="640000"
                 mcast_recv_buf_size="25000000"
                 mcast_send_buf_size="640000"
                 loopback="false"
                 discard_incompatible_packets="true"
                 enable_bundling="false"
                 max_bundle_size="64000"
                 max_bundle_timeout="30"
                 use_incoming_packet_handler="true"
                 use_outgoing_packet_handler="false"
                 ip_ttl="${jgroups.udp.ip_ttl:2}"
                 down_thread="false" up_thread="false"/>
              <PING timeout="2000"
                 down_thread="false" up_thread="false" num_initial_members="3"/>
              <MERGE2 max_interval="100000"
                 down_thread="false" up_thread="false" min_interval="20000"/>
              <FD_SOCK down_thread="false" up_thread="false"/>
              <FD timeout="10000" max_tries="5" down_thread="false" up_thread="false" shun="true"/>
              <VERIFY_SUSPECT timeout="1500" down_thread="false" up_thread="false"/>
              <pbcast.NAKACK max_xmit_size="60000"
                       use_mcast_xmit="false" gc_lag="0"
                       retransmit_timeout="300,600,1200,2400,4800"
                       down_thread="false" up_thread="false"
                       discard_delivered_msgs="true"/>
              <UNICAST timeout="300,600,1200,2400,3600"
                 down_thread="false" up_thread="false"/>
              <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
                       down_thread="false" up_thread="false"
                       max_bytes="400000"/>
              <pbcast.GMS print_local_addr="true" join_timeout="3000"
                       down_thread="false" up_thread="false"
                       join_retry_timeout="2000" shun="true"
                       view_bundling="true"
                       view_ack_collection_timeout="5000"/>
              <FC max_credits="2000000" down_thread="false" up_thread="false"
                  min_threshold="0.10"/>
              <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
              <pbcast.STATE_TRANSFER down_thread="false" up_thread="false" use_flush="false"/>
            </config>
          </attribute>
    
          <!--    The max amount of time (in milliseconds) we wait until the
                initial state (ie. the contents of the cache) are retrieved from
                existing members.
          -->
          <attribute name="InitialStateRetrievalTimeout">17500</attribute>
    
          <!--    Number of milliseconds to wait until all responses for a
                  synchronous call have been received.
          -->
          <attribute name="SyncReplTimeout">17500</attribute>
    
          <!--  Max number of milliseconds to wait for a lock acquisition -->
          <attribute name="LockAcquisitionTimeout">15000</attribute>
    
          <!--  Specific eviction policy configurations. -->
          <attribute name="EvictionPolicyConfig">
             <config>
                <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute>
                <attribute name="wakeUpIntervalSeconds">5</attribute>
                <name>flexiveMain</name>
                <!-- So default region would never timeout -->
                <region name="/_default_">
                   <attribute name="maxNodes">0</attribute>
                   <attribute name="timeToLiveSeconds">0</attribute>
                </region>
                <!-- cache region for contents -->
                <region name="/Division1/FxContent">
                    <attribute name="maxNodes">1000</attribute>
                    <attribute name="timeToLiveSeconds">1000</attribute>
                </region>
                 <!-- cache region for user configuration -->
                 <region name="/Division1/userConfig">
                     <attribute name="maxNodes">10000</attribute>
                     <attribute name="timeToLiveSeconds">1000</attribute>
                 </region>
             </config>
          </attribute>
       </mbean>
    </server>
    
                            

    If you want to use the external cache deplyoment you have to apply another modification: Due to classloading issues ([fleXive] uses JBoss Cache 2.x, while JBoss 4.2 uses JBoss Cache 1.x) you need to modify the deployment scanner in ${jboss.home}/server/default/conf/jboss-service.xml: replace the entry

                                <attribute name="URLComparator">org.jboss.deployment.DeploymentSorter</attribute>

    with

                                <attribute name="URLComparator">org.jboss.deployment.scanner.AlphaNumericDeploymentSorter</attribute>

    This ensures that the cache service is started after flexive.ear is deployed and thus can use its cache implementation classes (this deployment scanner allows to influence the deployment order with a numeric prefix, otherwise it's the same as the default one).

Notes for Glassfish 3+: The glassfish base directory is actually the glassfish subdirectory in the installation directory. For example, instead of ${glassfish.home}/domains/domain1 you would need to go to ${glassfish.home}/glassfish/domains/domain1. You can also skip the following section about required libraries if you intend to do only WAR deployments.

Three connection pools, two for the division (transactional and non-transactional) and one for the configuration database, have to be set up.

For MySQL copy and paste the following data-source definitions to a file (e.g. flexive-ds.xml) and update your database connection settings:

                <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//Sun Microsystems Inc.//DTD Application Server 9.0 Domain//EN" "sun-resources_1_3.dtd">
<resources>
    <!--
        Glassfish MySQL datasource configuration

        To add these datasource, start Glassfish v2, and execute
        ${glassfish.home}/bin/asadmin add-resources /path/to/flexive-mysql-ds.xml

        Please make sure mysql-connector-java-<version>-bin.jar is located in /domains/domain1/lib/ext (adapt the path to match your domain)
    -->

    <!-- Configure the global configuration datasource -->
    
    <jdbc-connection-pool
        name="flexiveConfiguration"
        datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"
        res-type="javax.sql.XADataSource"
		non-transactional-connections="false">
        <property name="user" value="root"/>
        <property name="password" value="a"/>
        <property name="url" value="jdbc:mysql://localhost:3306/flexiveConfiguration?useUnicode=true&amp;characterEncoding=utf8&amp;characterResultSets=utf8"/>
    </jdbc-connection-pool>

    <jdbc-resource pool-name="flexiveConfiguration" jndi-name="jdbc/flexiveConfiguration" enabled="true" object-type="user"/>


    <!-- Configure the first flexive division -->

    <jdbc-connection-pool
        name="flexiveDivision1"
        datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"
        res-type="javax.sql.XADataSource"
		non-transactional-connections="false">
        <property name="user" value="root"/>
        <property name="password" value="a"/>
        <property name="url" value="jdbc:mysql://localhost:3306/flexive?useUnicode=true&amp;characterEncoding=utf8&amp;characterResultSets=utf8"/>
    </jdbc-connection-pool>

    <jdbc-resource pool-name="flexiveDivision1" jndi-name="jdbc/flexiveDivision1" enabled="true" object-type="user"/>

    <!-- Configure the first flexive division non-XA datasource -->

    <jdbc-connection-pool
        name="flexiveDivision1NoTX"
        datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"
        res-type="javax.sql.DataSource"
		non-transactional-connections="true">
        <property name="user" value="root"/>
        <property name="password" value="a"/>
        <property name="url" value="jdbc:mysql://localhost:3306/flexive?useUnicode=true&amp;characterEncoding=utf8&amp;characterResultSets=utf8"/>
    </jdbc-connection-pool>

    <jdbc-resource pool-name="flexiveDivision1NoTX" jndi-name="jdbc/flexiveDivision1NoTX" enabled="true" object-type="user"/>

</resources>
            

Respectively apply these settings for H2:

                <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//Sun Microsystems Inc.//DTD Application Server 9.0 Domain//EN" "sun-resources_1_3.dtd">
<resources>
    <!--
        Glassfish H2 datasource configuration

        To add these datasource, start Glassfish v2, and execute
        ${glassfish.home}/bin/asadmin add-resources /path/to/flexive-h2-ds.xml

        Please make sure h2.jar is located in /domains/domain1/lib/ext (adapt the path to match your domain)
    -->

    <!-- Configure the global configuration datasource -->
    
    <jdbc-connection-pool
        name="flexiveConfiguration"
        datasource-classname="org.h2.jdbcx.JdbcDataSource"
        res-type="javax.sql.XADataSource"
		non-transactional-connections="false">
        <property name="user" value="sa"/>
        <property name="password" value="()"/>
        <property name="url" value="jdbc:h2:tcp://localhost:9092/~/flexiveConfiguration;MVCC=TRUE"/>
    </jdbc-connection-pool>

    <jdbc-resource pool-name="flexiveConfiguration" jndi-name="jdbc/flexiveConfiguration" enabled="true" object-type="user"/>


    <!-- Configure the first flexive division -->

    <jdbc-connection-pool
        name="flexiveDivision1"
        datasource-classname="org.h2.jdbcx.JdbcDataSource"
        res-type="javax.sql.XADataSource"
		non-transactional-connections="false">
        <property name="user" value="sa"/>
        <property name="password" value="()"/>
        <property name="url" value="jdbc:h2:tcp://localhost:9092/~/flexive;SCHEMA=flexive;MVCC=TRUE;LOCK_TIMEOUT=10000"/>
    </jdbc-connection-pool>

    <jdbc-resource pool-name="flexiveDivision1" jndi-name="jdbc/flexiveDivision1" enabled="true" object-type="user"/>

    <!-- Configure the first flexive division non-XA datasource -->
    <jdbc-connection-pool
        name="flexiveDivision1NoTX"
        datasource-classname="org.h2.jdbcx.JdbcDataSource"
        res-type="javax.sql.DataSource"
		non-transactional-connections="true">
        <property name="user" value="sa"/>
        <property name="password" value="()"/>
        <property name="url" value="jdbc:h2:tcp://localhost:9092/~/flexive;SCHEMA=flexive;MVCC=TRUE;LOCK_TIMEOUT=10000"/>
    </jdbc-connection-pool>

    <jdbc-resource pool-name="flexiveDivision1NoTX" jndi-name="jdbc/flexiveDivision1NoTX" enabled="true" object-type="user"/>

</resources>
            

While Glassfish is running, execute ${glassfish.home}/bin/asadmin add-resources /path/to/flexive-ds.xml to create the connection pools and JDBC resources for MySQL or ${glassfish.home}/bin/asadmin add-resources /path/to/flexive-h2-ds.xml if you are using the H2 database. You have to pass the absolute path to your XML file, otherwise Glassfish looks in its own config directory.

Should you need to edit or reset your datasources, you can do so in the Glassfish administration console under ResourcesJDBC.

This procedure is for those building [fleXive] from the source code.

  • Fetch the current source code: svn co http://svn.flexive.org/repository/flexive/trunk flexive-trunk

  • Copy the build.properties.sample file located in the root directory of the checked out sources (flexive-trunk) to build.properties.

  • Configure your datasource in build.properties and execute ant db.update all. This will recreate the database schema to be used (default: flexive).

  • To build and deploy the ear execute ant build-deploy.

    Don't forget to set the deploy.ear.path in build.properties file properly!

    • Note: to successfully compile [fleXive] JAVA 1.6 and Apache ANT 1.7 is needed

    • Be also sure to set the JDK_HOME and JAVA_HOME variables accordingly as explained in section Prerequisites.

In this chapter we will get hands-on and create a few demo applications with [fleXive]. These applications are part of the binary and source distributions, but you can also download them as separate projects:

  • helloworld is our canonical hello-world application,
  • tutorial01-documentstore show some of the document-handling capabilities in [fleXive],
  • products shows a localized frontend for a product database realized in [fleXive] that also has pretty URLs and a URL-based locale selector ("/de" and "/en"), and
  • announcement-submission shows a basic security setup of a publishing application with two usergroups ("editors" and "users").

To run any of these, unzip them to a local directory. Unzip the [fleXive] distribution from the download page in the same directory, remove the precompiled examples from flexive-dist/applications, and execute ant in the example directory. You can then deploy the dist/[example-name].ear file to your application server. For example (using Linux):

$ unzip flexive-dist.zip
$ unzip example-helloworld.zip
$ ls
flexive-dist helloworld

$ cd helloworld
$ ant
$ ls dist
helloworld.ear  helloworld-shared.jar  helloworld.war

Before setting out on your own, you might also want to read Chapter 4, Writing [fleXive] applications , which also explains how to use Apache Maven as your build tool.

Our first [fleXive] application implements a very simple blogging system with an input mask and a view for the submitted entries. We assume you have a flexive installation up and running on your local or on a remote machine. If not, follow the instructions in Chapter 2, Installing [fleXive]. The complete source code can be found here or in the subversion repository under src/examples/helloworld.

First we create a new [fleXive] project: go to the directory where you unpacked the distribution, and execute ant project.create. You will be prompted for the name of the new project. Enter helloworld and confirm your input. If all went well, the output should look like the following:

/tmp/flexive-dist$ ant project.create
Buildfile: build.xml

check:

project.create:
    [input] Name of the project you want to create:
helloworld
  [flexive]
  [flexive] Please confirm your input:
  [flexive] Project name:        helloworld
  [flexive] Base directory:      ../helloworld
  [flexive]
    [input] Are these settings correct? ([y], n)
y
    [mkdir] Created dir: /tmp/helloworld
     [copy] Copying 13 files to /tmp/helloworld
     [copy] Copied 29 empty directories to 6 empty directories under /tmp/helloworld
     [copy] Copying 1 file to /tmp/helloworld
     [copy] Copying 1 file to /tmp/helloworld
     [echo] Project helloworld created successfully. The project root directory is
     [echo] ../helloworld

BUILD SUCCESSFUL

The new project is created in the parent directory of the distribution, i.e. in ../helloworld, and looks like this:

flexive-dist
|-- ...

helloworld
|-- build.xml
|-- database.properties
|-- lib
|-- resources
|   |-- META-INF
|   |   |-- faces-config.xml
|   |   `-- web.xml
|   |-- messages
|   |-- scripts
|   |   |-- library
|   |   |-- runonce
|   |   `-- startup
|   `-- templates
|-- src
|   `-- java
|       |-- ejb
|       |   `-- ...
|       |-- shared
|       |   `-- ...
|       `-- war
|           `-- ...
`-- web
    `-- index.xhtml

For the web interface of the application we use JSF 1.2, using Facelets for creating the JSF views (i.e. we use plain XHTML templates instead of JSP). [fleXive] includes components for rendering and editing [fleXive] contents in JSF applications, although you could also use other web framework like Struts or Tapestry - you'd just miss the convenient UI components that allow a rapid creation of web applications based on [fleXive].

If you are not familiar with JSF tag libraries please refer to the Tag Library Documentation or try the JSF section of the Java EE 5 Tutorial.

The main page renders all available blog entries and shows a link to a form for creating new entries. Facelets' <ui:repeat/> tag is used to iterate over the rows of the datamodel returned by the JSF bean we created in the previous section. A row of the datamodel provides indexed access to the columns selected in the search query of the previous section. In this case, #{columns[0]} would be the content primary key (@pk), #{columns[1]} returns the entry title, and so on. The source for this file can be found under web/index.xhtml.

                <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:c="http://java.sun.com/jstl/core"
      xmlns:fx="http://www.flexive.com/jsf/core">
<head>
    <fx:includes/>
</head>

<body>

<h:outputLink value="create.xhtml">Create a new entry</h:outputLink>

<!-- Render available blog postings, as returned by helloWorldBean.blogEntries -->

<!-- The Facelets ui:repeat tag is used to iterate over arrays, lists and JSF datamodels -->
<ui:repeat value="#{helloWorldBean.blogEntries}" var="column">
    <h3>
        <!-- Entry title. Access the value of each column through the expression language
         and the column index -->
        #{column[1]}
    </h3>
        <pre>#{column[2]} <!-- Entry text -->

            <i>#{column[3]}</i> <!-- Creation date -->
        </pre>
</ui:repeat>

<p><h:outputLink
        onclick="window.open('http://wiki.flexive.org/confluence/display/FX/Listing+blog+entries', 'In_depth_explanation', 'width=950,height=600,left=50,top=200,scrollbars=yes,resizable=yes');"
        value="#">
    What happens on this page?
</h:outputLink>
</p>

</body>

</html>
            

The input form for creating new postings is placed in web/create.xhtml and uses the <fx:content/> and <fx:value/> components provided by [fleXive] to create a simple input form for [fleXive] contents. All you need to provide is the name of the type you want to use as template (blogEntry) and the references to the properties to be rendered (entryTitle, entryText). Finally, a JSF command link is used to render a button for saving the entered data. The save command is executed by a [fleXive] system bean, fxContentViewBean.

                <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:c="http://java.sun.com/jstl/core"
      xmlns:fx="http://www.flexive.com/jsf/core">
<head>
    <title>Flexive Helloworld Application</title>
    <fx:includes yui="true"/>
</head>

<body class="yui-skin-sam">

<h:form>
    <!-- Display all JSF messages -->
    <h:messages/>

    <p><h:outputLink value="index.xhtml">Back to blog</h:outputLink></p>

    <!-- The fx:content tag references our type "Blog Entry" -->
    <fx:content typeName="blogEntry" var="entry">

        <!-- This renders an html input field for the type's property "Entry Title" -->
        <fx:value property="entryTitle"/>

        <!-- This renders an html input field for the type's property "Entry Text" -->
        <fx:value property="entryText"/>

        <!-- Save content using the FxContentViewBean, pass the content instance
             stored in component_content via f:setPropertyActionListener -->
        <h:commandButton action="#{fxContentViewBean.save}" value="Publish">
            <f:setPropertyActionListener target="#{fxContentViewBean.content}" value="#{entry_content}"/>
        </h:commandButton>

    </fx:content>
</h:form>

<p><h:outputLink
        onclick="window.open('http://wiki.flexive.org/confluence/display/FX/Creating+a+blog+entry', 'In_depth_explanation', 'width=950,height=600,left=50,top=200,scrollbars=yes,resizable=yes');"
        value="#">
    What happens on this page?
</h:outputLink>
</p>

    <fx:yuiSetup/>

</body>
</html>
            

To compile and deploy the framework and the example application simply run ant in the project directory (helloworld). If the compilation was successful, you find your application packaged under dist/helloworld.ear. You can then deploy this EAR to your application server, assuming that you followed the installation instructions. If you already have a [fleXive] EAR deployed in this instance, be sure to undeploy it before you deploy helloworld.ear. If you used the Java-based installer, you can deploy and run the application with ant deploy.jetty run.jetty.

If you need to setup or reset the database schema, update your database connection settings in hello-world/database.properties or flexive-dist/database.properties and run ant db.create in the corresponding directory.

After deploying the application, point your browser to http://localhost:8080/flexive/adm/ to open the administration GUI (default credentials: supervisor/supervisor), or http://localhost:8080/helloworld/ to open the application. Congratulations! You should see a link to the input mask.

Now that you are familiar with some basic concepts of [fleXive], we are ready to explore more features. The first tutorial application introduces

  • binary support in [fleXive] contents,

  • combined create/edit forms, as well as

  • a more sophisticated way of rendering results, like automatic preview images of contents.

Our task for this tutorial is to create a centralized document store that allows us to store documents, images or videos and provides a web-based user interface for it. In the first tutorial we will implement the basic functionality, and then further refine and extend the features in following tutorials. Figure 3.1, “The document store application, version 1” shows the end result of the first tutorial, with the help of some stylesheets that can be found in the tutorial source tree.


First we create new project called tutorial01-documentstore. Use your local installation of the [fleXive] distribution as described in the section called “Your first [fleXive] application” to create a new project. The complete source code can be found here. We start by defining our data model used for storing documents. We assign the standard caption property /caption for document captions, and add a Binary property. We create the type in the run-once script resources/scripts/runonce/tutorial001.groovy:

                    /**
 * Initialization script for the tutorial01 application.
 *
 * @author Daniel Lichtenberger (daniel.lichtenberger@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
 * @version $Rev: 1106 $
 */

import com.flexive.shared.scripting.groovy.*
import com.flexive.shared.value.*
import com.flexive.shared.structure.*

new GroovyTypeBuilder().document01(
        label: new FxString(true, "Tutorial document 01"),
        usePermissions: false)
{
    // assign the root caption under /caption
    caption(assignment: "ROOT/CAPTION")

    // store the mandatory binary under /file
    file(dataType: FxDataType.Binary,
         multiplicity: FxMultiplicity.MULT_1_1,
         label: new FxString(true, "File"))
}

                

The most interesting parts of this tutorial are the overview and upload pages. On the first we display all uploaded document objects, the latter allows the user to upload new files or edit existing ones.

The index page of the application renders a link to an empty upload form for uploading new document, and also renders all document objects already uploaded. Note that in the first version of the tutorial, security is completely disabled, thus every user can see and edit all document objects in the system.

Similar to the helloworld example application, we iterate over the rows of the result data model using <ui:repeat>. But this time, we render the result using the <fx:resultValue> tag, which essentially renders read-only <fx:fxValueInput> components for more sophisticated output formatting. For the document browser, we select four columns in our content query:

  1. @pk,

  2. document01/file,

  3. caption, and

  4. created_at.

When we pass a value of the second column (document01/file) to the <fx:resultValue> component, it actually renders an inline image that displays a preview of the uploaded file. For image data types, this is a thumbnail, for other types like documents or presentations, it is an icon corresponding to the document type. So to render a list of thumbnail preview images, we write the following:

<ui:repeat var="row" value="#{tutorialBean.documents}">
    <fx:resultValue value="#{row[1]}"/>
</ui:repeat>

We can still write the result values as literal JSF-EL expressions when necessary, so for example to add the caption after each image it would be sufficient to write #{row[2]}. However, the <fx:resultValue> tag adds extra functionality like date formatting or support for inline HTML properties (otherwise HTML contained in an FxHTML would be escaped by Facelets), so it's generally a good idea to use <fx:resultValue> for any result value rendered in the response.

Our web/index.xhtml lists all available documents and adds a link to the upload form.

                    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:c="http://java.sun.com/jstl/core"
      xmlns:fx="http://www.flexive.com/jsf/core">
<head>
    <!-- Add flexive includes -->
    <fx:includes/>

    <!-- Add our own stylesheet for the result page -->
    <link rel="stylesheet" type="text/css" href="css/tutorial01.css"/>
</head>

<body>
<!-- Output JSF error or info messages here -->
<h:messages globalOnly="true"/>

<p class="message">
    Welcome to tutorial 1 - the first version of our document data store.
</p>

<!-- The main menu -->
<ul id="mainmenu">
    <li>
        <h:outputLink value="upload.xhtml">Upload document</h:outputLink>
    </li>
</ul>

<!-- Render all available document objects, provided by #{tutorialBean.documents} -->
<h:form id="frm">
    <ul class="documents">

        <!-- Iterate over all document objects -->
        <ui:repeat var="row" value="#{tutorialBean.documents}">
            <li>
                <!-- Render the file (preview image) -->
                <fx:resultValue id="preview" value="#{row[1]}"/>

                <!-- Render the document caption -->
                    <span class="caption">
                        <fx:resultValue id="caption" value="#{row[2]}"/>
                    </span>

                <!-- Add an edit button below the image -->
                <h:commandLink action="edit" styleClass="editButton">
                    <!--
                        Load the content instance of the current row and store it in
                        #{fxContentViewBean.content}. The edit page will then use this
                        content instance.
                        Note that this listener will only be fired when the user actually
                        clicks on the commandLink.
                    -->
                    <f:setPropertyActionListener target="#{fxContentViewBean.content}"
                                                 value="#{fxSystemBean.content[row[0]]}"/>
                    Edit...
                </h:commandLink>
            </li>
        </ui:repeat>
    </ul>
</h:form>

<p style="clear:both; padding-top:25px;"><h:outputLink
        onclick="window.open('http://wiki.flexive.org/confluence/display/FX/Generating+thumbnails', 'In_depth_explanation', 'width=950,height=600,left=50,top=200,scrollbars=yes,resizable=yes');"
        value="#">
    What happens on this page?
</h:outputLink>
</p>

</body>
</html>
                

The upload form allows both to create new document objects by uploading files and editing existing ones. It works exactly as the form for entering new blog posts in the helloworld tutorial, except that we specify an explicit content instance that may be set from an edit link on the overview page:

<fx:content typeName="document01"
                        content="#{fxContentViewBean.content}"
                        var="document">
                    

When this page is opened through the "Upload" link on the front page (or by entering the URL to /upload.xhtml in the browser location bar), #{fxContentViewBean.content} evaluates to null and a new content instance of type document01 will be initialized. Otherwise, the content instance from retrieved from the fxContentViewBean will be edited. A call to fxContentViewBean.save creates or updates the content instance in the database.

The upload form renders two new input components:

  • a file upload form, and

  • a multilanguage input for the caption property.

For file uploads to work, you need to set the enctype attribute of the HTML form to multipart/form-data:

<h:form enctype="multipart/form-data">

We then create the upload page under web/upload.xhtml and add a basic content editor for creating new documents. The input component supports binary properties, but you have to set the form encoding to multipart/form-data.

                        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:c="http://java.sun.com/jstl/core"
      xmlns:fx="http://www.flexive.com/jsf/core">
<head>
    <!-- Add flexive includes -->
    <fx:includes/>

    <!-- Add our own stylesheet for the result page -->
    <link rel="stylesheet" type="text/css" href="css/tutorial01.css"/>
</head>

<body>

<h:messages globalOnly="true"/>

<h:form enctype="multipart/form-data">

    <ul id="mainmenu">
        <li>
            <h:outputLink value="index.xhtml">Return to overview</h:outputLink>
        </li>
    </ul>


    <!--
         Create a new content of type document01 or edit an existing content instance if set in fxContentViewBean.
    -->
    <fx:content typeName="document01" content="#{fxContentViewBean.content}" var="document">

        <fx:fieldSet legend="Document Upload">
            <!-- Render the input form -->
            <fx:value property="file"/>
            <fx:value property="caption"/>

            <!-- Render the submit button -->
            <fx:formRow>
                <h:commandButton action="#{fxContentViewBean.save}" value="Save">
                    <f:setPropertyActionListener target="#{fxContentViewBean.content}" value="#{document_content}"/>
                    <f:setPropertyActionListener target="#{fxContentViewBean.successMessage}"
                                                 value="Successfully saved the document."/>
                </h:commandButton>
            </fx:formRow>

        </fx:fieldSet>

    </fx:content>

</h:form>

<p><h:outputLink
        onclick="window.open('http://wiki.flexive.org/confluence/display/FX/Uploading+documents', 'In_depth_explanation', 'width=950,height=600,left=50,top=200,scrollbars=yes,resizable=yes');"
        value="#">
    What happens on this page?
</h:outputLink>
</p>

</body>
</html>
                    

To compile and deploy the framework and the example application simply run ant in the project directory (tutorial01-documentstore). If the compilation was successful, you find your application packaged under dist/tutorial01-documentstore.ear. You can then deploy this EAR to your application server, assuming that you followed the installation instructions. If you already have a [fleXive] EAR deployed in this instance, be sure to undeploy it before you deploy tutorial01-documentstore.ear. If you used the Java-based installer, you can deploy and run the application with ant deploy.jetty run.jetty.

If you need to setup or reset the database schema, update your database connection settings in tutorial01-documentstore/database.properties or flexive-dist/database.properties and run ant db.create in the corresponding directory.

After deploying the application, point your browser to http://localhost:8080/flexive/adm/ to open the administration GUI (default credentials: supervisor/supervisor), or http://localhost:8080/tutorial01-documentstore/ to open the application.

There are two convenient ways to start a new [fleXive] project:

  1. to use the [fleXive] distribution and create new projects with Apache Ant, or

  2. to use Apache Maven and add [fleXive] to your project dependencies.

While 1) offers you out-of-the-box support for all [fleXive] features such as run-once scripts, plugins, weblet resources and so on, 2) is more modular and allows you to use [fleXive] just as any other library. Of course you can also use [fleXive] in your own enterprise application by adding the libraries.

The easiest way to get started with [fleXive] is to use the Java-based installer from the download page. The installation directory contains the [fleXive] distribution in the flexive-dist directory. In this chapter we will concentrate on creating new [fleXive] applications.

Looking at your local [fleXive] distribution directory, you will find the following directory layout:

.
|-- META-INF/
|-- build.xml
|-- applications/
|-- extlib/
|-- lib/
|-- templates/

build.xml

Contains the build file for creating new projects and components. To get started, simply execute ant in your flexive-dist/ directory.

applications

Contains all [fleXive] applications that should be included in the EAR file. The standard distribution includes the administration GUI in this directory. To remove an application from the EAR, simply remove it from this directory and rebuild the EAR file.

lib

Contains all [fleXive] libraries packaged as JAR files.

extlib

Contains all third-party libraries required for compiling and running [fleXive] applications. Note that not all libraries in this directory will be packaged into the final application archive, for example the JSF API package is only required for compiling JSF applications.

templates

Contains project and component templates used by the build system.

META-INF

Currently this directory only holds a template application descriptor for [fleXive] applications.

Before proceeding to create a new [fleXive] application, make sure you have a working build system, i.e. at least

For running the application you can use Jetty and the integrated H2 database if you used the installer. Otherwise, you have to configure an application server and a database according to Chapter 2, Installing [fleXive].

To get started with your first [fleXive] application, open a command shell and change to the directory containing the [fleXive] distribution. Type ant. You should be greeted by the welcome page of the [fleXive] build system:

Buildfile: build.xml

info:
  [flexive]
  [flexive] Welcome to the flexive build tool. Feel free to use the following build targets:
  [flexive]
  [flexive] project.create
  [flexive]     Creates a new flexive project directory.
  [flexive]
  [flexive] component.create
  [flexive]     Creates a new flexive UI component directory.
  [flexive]
  [flexive] db.create
  [flexive]     Create or reset the database schema of a flexive division.
  [flexive]     Warning: if the schema already exists, it will be dropped (i.e. you will lose all data
  [flexive]     stored in the schema).
  [flexive]
  [flexive] db.config.create
  [flexive]     Create or reset the global flexive configuration schema.
  [flexive]
  [flexive] ear
  [flexive]     Create a flexive.ear distribution including any flexive application stored in
  [flexive]     flexive-dist/applications.
  [flexive]
  [flexive] glassfish.libs
  [flexive]     Copies libraries needed for Glassfish compatibility to a directory
  [flexive]
    [input] Please enter a target name, or quit to exit:

Let's create a new project. Type project.create and hit return. You will be asked for a project name. This name will be used as the root directory name for the project, so be careful to include only characters that may appear in filenames and URLs. For a start, enter flexive-test.

[fleXive] will create the project folder in the same directory where the [fleXive] distribution is stored, i.e. in the current parent directory. The major reason for this is that the project references the distribution directory, i.e. it includes all the libraries from the distribution directory and does not use its own copies. Thus new [fleXive] projects use little disk space, and you need only one [fleXive] distribution for all your projects.

After confirming your selection, the root directory layout for flexive-test will be created. Your screen should look approximately like this:

    [input] Please enter a target name, or quit to exit:
project.create

check:

project.create:
    [input] Name of the project you want to create:
flexive-test
  [flexive]
  [flexive] Please confirm your input:
  [flexive] Project name:        flexive-test
  [flexive] Base directory:      ../flexive-test
  [flexive]
    [input] Are these settings correct? ([y], n)
y
    [mkdir] Created dir: /home/daniel/dev/idea-workspace/flexive/flexive-test
     [copy] Copying 4 files to /home/daniel/dev/idea-workspace/flexive/flexive-test
     [copy] Copied 14 empty directories to 9 empty directories under /home/daniel/dev/idea-workspace/flexive/flexive-test
     [copy] Copying 1 file to /home/daniel/dev/idea-workspace/flexive/flexive-test
     [copy] Copying 1 file to /home/daniel/dev/idea-workspace/flexive/flexive-test
     [echo] Project flexive-test created successfully. The project root directory is
     [echo] ../flexive-test

BUILD SUCCESSFUL

When the build tool has finished successfully, go to the newly created directory, e.g. cd ../flexive-test. The directory structure contains a blank project structure, which looks like the following:

.
|-- build.xml
|-- lib
|-- resources
|   |-- META-INF
|   |   |-- faces-config.xml
|   |   |-- template.taglib.xml.sample
|   |   `-- web.xml
|   |-- messages
|   |-- scripts
|   |   |-- library
|   |   |-- runonce
|   |   `-- startup
|   `-- templates
|-- src
|   `-- java
|       |-- ejb
|       |-- shared
|       `-- war
`-- web
    `-- index.xhtml

Before examining the directory structure, let's do a quick test if the the environment is working. Type ant. The build should complete successfully, leaving you with some artifacts in the dist/ subdirectory:

  • flexive-test.ear,

  • flexive-test-shared.jar, and

  • flexive-test.war.

If you used the installer, you can instantly boot the application with ant deploy.jetty run.jetty. If you didn't use the installer, or you want to reset the database schemas during development, type ant db.create db.config.create and when prompted for the database schema use the default name, flexive. After the command completes successfully, your can deploy flexive-test.ear to your application server and have a working (albeit empty) [fleXive] application, including the administration GUI.

The project root directory contains a build file, build.xml, that can be customized for the project. By default, it builds JAR files for all layers, including an EAR archive. The major subdirectories are:

src/java

contains the Java sources of the project. They are split up by layer, i.e. there are three distinct source trees for the EJB, web, and shared classes. This is especially useful for IDEs with support for multiple project modules, where you can also specify wanted (and forbidden) relationships between the layers.

web

contains the documents for the web application (if any).

lib

contains additional libraries and components used by the project.

resources

is the root folder for various project resources:

resources/scripts

contains the run-once, startup and library scripts of the application,

resources/messages

contains the application's localized message resources that can be accessed with the fxMessageBean,

resources/META-INF

contains the application's configuration files, mostly for the web layer,

resources/templates

is the standard folder for Facelets templates. You can choose any folder of course if you like, but then you'd have to modify the build script.

Your [fleXive] project comes with basic project files for the Eclipse and IntelliJ IDEA Java IDEs.

If this is your first Eclipse project with [fleXive], first you have to define a new user library that includes the JAR files of your [fleXive] distribution. We also could include these files in the individual project classpath, but this way is much cleaner and easier to work with especially for multiple projects.

  1. Please go to WindowPreferencesJavaBuild PathUser Libraries.

  2. Click New... and enter flexive for the library name. Press OK.

  3. Select the created library entry, and press the Add JARs... button.

  4. Go to your [fleXive] distribution directory, and select all JAR files in the lib/ folder (you can use shift-select here).

  5. Repeat the last two steps and add all JAR files of the extlib/ folder and the flexive-plugin-jsf-core.jar from the applications/ folder to the user library.

  6. In the User Libraries page, click OK. You have now defined a global flexive library that includes all required JAR files for a [fleXive] application. Note that this is only available within the IDE, the actual build files do not use the IDE-specific library definitions.

Assuming that both your flexive-dist directory and the newly created project reside in your current Eclipse workspace directory, you can add the project following these steps:

  1. Open FileImport...+GeneralExisting Projects into Workspace

  2. Select the project root directory.

  3. Press Finish. Eclipse should now open the project in the workspace. To build the project EAR file using Ant, execute ProjectBuild all.

    To check if the project and library has been loaded correctly, try to open the EJBExampleBean class using NavigationOpen Type... ( Shift+Ctrl+T ) Eclipse should display the source code without errors.

If you created a new [fleXive] application with the tools of this chapter, it will include a buildfile for Apache Ant that compiles and packages the application into JAR and EAR files as described in the previous section. It uses a shared generic build file from the flexive-dist directory that is customized by setting a few Ant properties:

fxProject

The project name, in our example flexive-test.

flexive.dist.dir

The [fleXive] distribution directory. By default this is a relative location. Keep this and do not alter the directory name if you want to keep the build environment independent from the actual workspace location.

[basename].shared.disabled , [basename].ejb.disabled , [basename].war.disabled , [basename].ear.disabled

If any of these properties is set, the corresponding archive file will not be generated. [basename] is the project name as set in the fxProject property, in our example application we would skip the EAR file generation using <property name="flexive-test.ear.disabled" value="1"/>.

The resulting JAR and EAR files will be stored in the dist/ directory of the project. If you deploy the EAR file into your application server, please make sure that no other [fleXive] EAR is deployed to prevent conflicts.

The default Apache Ant target builds the entire application, including an EAR file that can be deployed into your application server. Additional tasks are provided for setting up the database schemas.

package (default)

Build all archives, except disabled ones as described in the previous section.

help

Display a short help message and offer documentation of the most relevant tasks.

db.create

Create or reset a [fleXive] division database schema. By default, the first division uses the schema flexive.

db.config.create

Create or reset the global [fleXive] configuration schema, flexiveConfig.

Apache Maven is a build tool like Apache Ant, but with a completely different approach to managing software builds: where Apache Ant is like a procedural programming language for describing how to build your system, Apache Maven is more of a fully-fledged assembly line for building, packaging, testing, and running software projects.

The Maven repository at http://repo.flexive.org/maven2/ contains all [fleXive] libraries with correct dependencies set up. To use it in your Maven project, add the following repository to your pom.xml:

<repository>
    <id>maven.flexive.org</id>
    <name>Flexive repo</name>
    <url>http://repo.flexive.org/maven2</url>
    <layout>default</layout>
</repository>

The repository contains all released versions, including snapshots for all active branches that are updated from our internal continuous integration servers. Use these -SNAPSHOT revisions to use the latest features and bugfixes, but don't use them in a final product!

To resolve all dependencies of [fleXive], you also need to add the following repositories from java.net and JBoss:

<repository>
    <id>maven2-repository.dev.java.net</id>
    <name>Java.net Repository for Maven</name>
    <url>http://download.java.net/maven/2/</url>
    <layout>default</layout>
    <snapshots>
        <updatePolicy>never</updatePolicy>
    </snapshots>
</repository>

<repository>
    <id>maven2-jboss-nexus</id>
    <name>JBoss Maven Repository</name>
    <url>http://repository.jboss.org/nexus/content/groups/public-jboss</url>
    <layout>default</layout>
    <snapshots>
        <updatePolicy>never</updatePolicy>
    </snapshots>
</repository>

<repository>
    <id>maven2-jboss</id>
    <name>JBoss Maven Repository</name>
    <url>https://repository.jboss.org/nexus/content/repositories/deprecated</url>
    <layout>default</layout>
    <snapshots>
        <updatePolicy>never</updatePolicy>
    </snapshots>
</repository>

<repository>
    <id>maven-dev-repository.dev.java.net</id>
    <name>Java.net Dev Repository for Maven</name>
    <url>https://maven-repository.dev.java.net/repository/</url>
    <layout>legacy</layout>
    <snapshots>
        <updatePolicy>never</updatePolicy>
    </snapshots>
</repository>

Archetypes provide a quick start for new projects. Currently we offer a archetype for a full enterprise application with web and console frontends. Since the archetype includes setup scripts for the H2 database, it can be used without external dependencies like MySQL. However, for administration tasks such as database setup you currently have to use the tools provided by the [fleXive] distribution.

The archetypes are (as the rest of the Maven modules) still under development, so please report rough edges or missing features in our issue tracker.

Maven snapshot versions

The snapshot versions in our Maven repository are updated automatically from our internal Continuous Integration server (Hudson) with the latest stable build of the corresponding branch. A build is considered "stable" in this context when no testcase fails. The snapshot versions are a convenient way of getting and testing the latest features, however these builds are not tested manually and may cause all kinds of havoc including data loss. If possible, it is recommended to use stable versions.

The archetype flexive-archetype-ear creates a multi-module enterprise application, which will be deployed as an EAR. It also allows easy integration testing with OpenEJB and offers a standalone webserver using Jetty and H2.

To get started, create a new project using the following command:

mvn archetype:generate -DarchetypeGroupId=com.flexive -DarchetypeArtifactId=flexive-archetype-ear -DarchetypeVersion=0.8 -DarchetypeRepository=http://repo.flexive.org/maven2/

This will prompt you for the artifact and group IDs and create a new project in the current directory. The [fleXive] version to be used can be specified in the main module's pom.xml file. By default, it is set to the current release required for all features (currently this is 3.1.4). For a list of available versions, please look at the Maven repository (e.g. here).

The following commands (issued in the project directory) will walk you through the most important features:

mvn package

Compile and package the application. The resulting EAR file can be deployed in any supported application server (for setup instructions, consult Chapter 2, Installing [fleXive]).

mvn install

Compile, package and install the application. If executed for the first time, the H2 database schemas will also be created in the database/h2 subdirectory.

mvn install -Pdb-setup-h2 (since 3.1)

Compile, package and install the application. Manually activate the profile db-setup-h2 to reset the H2 database in the database/h2 subdirectory. (You can also force a reset of the H2 database by deleting the database/h2 directory and running mvn install.)

cd war , mvn jetty:run (since 3.1)

Start an instance of the Jetty WebServer to deploy our application (including the administration GUI) and OpenEJB. The available applications can be browsed at http://localhost:8080.

Additional information on working with the Maven archetypes can be found in the following blog entries:

Warning: version 3.0.x and embedded containers

Please note that some features are dependent on the [fleXive] version: H2/OpenEJB/Jetty support is only available in 3.1 or later. In order to build your project with [fleXive] 3.0.x, you have to disable the database and consoleapp modules in your root pom.xml. Then you can package an EAR file using the mvn package command.

The archetype flexive-archetype-war provides a base for packaging a [fleXive] application for EJB 3.1-compatible containers that support EJB deployment for WAR applications.

The resulting WAR runs also on JavaEE 6 web profile containers, as long as you don't define remote interfaces (or use other features that are missing in the web profile, such as message driven beans).

Currently the support is targeted on Glassfish 3+ (being the reference implementation for JavaEE 6), but no Glassfish-specific features are required. To create a new project:

mvn archetype:generate -DarchetypeGroupId=com.flexive -DarchetypeArtifactId=flexive-archetype-war -DarchetypeVersion=0.8 -DarchetypeRepository=http://repo.flexive.org/maven2/

Invoke mvn package to create the WAR file, and deploy it to the application server.

While you can browse all artifacts at http://repo.flexive.org/maven2/, the following list enumerates those you will most likely end up using in your application.

flexive-ejb

The [fleXive] EJB layer (packaging: ejb). Add this to your EAR module.

<dependency>
    <groupId>com.flexive</groupId>
    <artifactId>flexive-ejb</artifactId>
    <version>3.1.4</version>
    <type>ejb</type>
</dependency>
flexive-shared

The [fleXive] shared classes, including all EJB interfaces. Use this artifact in modules that need to access [fleXive] via EJB, but are not EJBs themselves (e.g. web modules).

<dependency>
    <groupId>com.flexive</groupId>
    <artifactId>flexive-shared</artifactId>
    <version>3.1.4</version>
    <type>jar</type>
</dependency>
flexive-backend

The [fleXive] administration GUI, including all required libraries and the actual WAR package.

<dependency>
    <groupId>com.flexive</groupId>
    <artifactId>flexive-backend</artifactId>
    <version>3.1.4</version>
    <type>jar</type>
</dependency>
flexive-plugin-jsf-core

The JSF component library (includes flexive-web-shared).

<dependency>
    <groupId>com.flexive</groupId>
    <artifactId>flexive-plugin-jsf-core</artifactId>
    <version>3.1.4</version>
    <type>jar</type>
</dependency>
flexive-web-shared

Shared (framework agnostic) web classes, including authentication filters, servlets for delivering [fleXive] contents.

<dependency>
    <groupId>com.flexive</groupId>
    <artifactId>flexive-web-shared</artifactId>
    <version>3.1.4</version>
    <type>jar</type>
</dependency>
flexive-extractor-documents

Document extractors (e.g. for PDF files). Includes libraries to extract the text from a binary document format for full-text search and renderers for preview generation. Matching documents are stored as instances of the "Document" type.

<dependency>
    <groupId>com.flexive</groupId>
    <artifactId>flexive-extractor-documents</artifactId>
    <version>3.1.4</version>
    <type>jar</type>
</dependency>
flexive-extractor-audio (since 3.1.2)

Audio extractors (e.g. for MP3 files). Extracts meta information like bitrate or album title from common audio file formats and stores it in the "Audio" document type.

<dependency>
    <groupId>com.flexive</groupId>
    <artifactId>flexive-extractor-audio</artifactId>
    <version>3.1.4</version>
    <type>jar</type>
</dependency>

[fleXive] is a comprehensive JavaEE 5 library with an EJB3 interface and a supplementing JSF component library. It is based on current Java enterprise technologies and focuses on flexibility and extensibility. Chapter 6, The [fleXive] core components and Chapter 7, JSF Support explain all key concepts services relevant to users of the [fleXive] framework.

The core of [fleXive] is a collection of EJB3 beans (called engines) that offer services such as content creation and retrieval, search queries and the definition of data structures. The client is usually a web application or another EJB3 that extends or embeds [fleXive] functionality.

The [fleXive] core is based on the following EJB engines:

  • The content engine implements the persistency layer that is used to create, update and delete [fleXive] contents. It is supported by an extensive security layer that offers fine-grained permission controls for all contents managed by [fleXive].

  • The content engine is supplemented by the search engine to search for contents in a SQL-like query language.

  • The structure engine is responsible for creating and editing the dynamic data structures used by the content engine.

  • The tree engine provides a scalable implementation for organizing contents in a hierarchical tree structure.

  • Further engines extend the basic functionality: the scripting engine allows to fire user-defined code at almost any event in the content engine, user management provides access to the user directory, the workflow engine implements workflows for content instances, and the configuration engine stores user preferences and system configuration parameters.

Table of Contents

User Management
Creating a user
Updating a user
Removing a user
Security
Overview
Authentication / FxContext / UserTickets
Divisions
Mandators
ACLs - Access Control Lists
Role Based Security
Structure Engine
Overview
Types
Root Type
Type parameters
Creating a new type
Data types
Handling binary content
Select lists
Select list parameters
Select list item parameters
Select list creation and persistance
Select list item permissions
Multiplicity
Properties and property assignments
Conceptual explanation of property assignments
Property parameters
Uniqueness of values
Groups and group assignments
Group parameters
Group modes
Property and group options
The GroovyTypeBuilder
GroovyTypeBuilder Syntax
GroovyTypeBuilder Attributes
Property and Group Creation - Default Values
GroovyTypeBuilder Usage Examples
The GroovyOptionBuilder
Content Engine
Overview
Using FxContent
Primary keys and versions
Search Engine
An introduction to FxSQL
Select user-defined columns
Select all columns
Fulltext search
Properties versus assignments
Content permissions
Metadata
Lock information
Tree Search
Briefcase Search
Date and Time Functions
Type conditions
Select list properties
Resolving system properties
Filters
Order by
Comments
Operator table
Literal value formatting
SqlQueryBuilder: Building queries in Java
Conditions
Filters
Nested conditions
Search parameters
The Groovy query builder
Working with search results
Accessing result rows
Projections
Briefcase Engine
Tree Engine
Security
Scripting
Edit and Live modes
Examples
Scripting Engine
Overview
Events and Bindings
Run-Once and Startup Scripts
Workflow Engine
Conceptual Overview
Creating and updating workflows
Configuration Engine
Conceptual Overview
Working with configuration parameters
Object parameters

If you remove all eyecandy created by more or less sophisticated user interfaces you will discover the driving force behind [fleXive]: the core layer. It is implemented at the EJB layer and is provided by stateless session beans. These engines serve as abstractions to concrete implementations (usually using some form of singleton pattern) for different databases or algorithms used.

The areas covered are structure definition, content manipulation, SQL like queries, organizing contents in trees, scripting, workflow, user management and how security is handled.

Handling users and user information is a basic business of virtually every major software system in use today. At this juncture [fleXive] is no exception. User management in [fleXive] is based on accounts – that is a user and its basic information (name, login name, contact data, e-mail addresss, ...) are stored in accounts.

In the table below you see the listing of all attributes an account in [fleXive] owns.

Table 6.1. Account Attributes
Attribute Description
id, name, login name, e-mail, description, contactDataId Some basic attributes. The Id uniquely identifies the account. The description allows you to characterize the account while the contactDataId identifies the contact data (e.g. postal address, telephon number) linked to this user. Name and login name stand for themselves.
mandatorId The mandator the account belongs to. Note that every account has to be assigned to exactly one mandator.
language Identifies the preferred language for the account. Whenever there are available user interface translations or contents in several languages the system will select to display the ones in the language specified here, if available.
active An account can be set to status inactive. Thus no login is possible while it is not activated again. Note: both flags, active and validated, have to be set to true for the login to work.
validated Flags for admins indicating that the user data was audited and is no fake. Setting this flag to false prevents a login for the corresponding account until the flag is set to true. This setting can be used when auto-creating accounts to force validation of the account data. Note: both flags, active and validated, have to be set to true for the log in to work.
validFrom, validTo The valid from/to dates may be used to define a time periode in which the user may log in.
defaultNodeId The desired start node in the tree for the user.
allowMultiLogin True if multiple logins for the account are possible at the same time.
updateToken The update token may be used in external API calls modifying this account to improve security.

There are two system defined accounts:

  • the guest user. Everyone who is not logged in is treated as GUEST.

  • the supervisor. This user is in all roles and may operate on all mandators.

These two accounts can not be removed.

In the following we will look at how to create, update and remove accounts.

Creating a user requires special rights. More precisely only callers (i.e. a user) in role ACCOUNT MANAGEMENT may create users, and only for their mandator. An exception is a user in the role GLOBAL_SUPERVISOR who may create users for all mandators. An example of how to create a new user is given next.


After the account creation one can assign the roles the account is in and the groups it belongs to. For a detailed explanation of the meaning and functioning of roles and groups refer to the the section called “Security”.

For assigning a role to an account the following rules apply:

  • the caller must be in role ACCOUNT MANAGEMENT

  • the account/user has to belong to the callers mandator

  • the caller may only assign roles that he is assigned to himself

GROUP_GLOBAL_SUPERVISOR may set the roles for all users in the system.

Assigning a group to an account presumes the following prerequisites:

  • the caller must be in role ACCOUNT MANAGEMENT

  • the account/user has to belong to the callers mandator

  • the caller may only assign groups that also belong to his mandator, plus GROUP_EVERYONE and GROUP_OWNER

GROUP_GLOBAL_SUPERVISOR may set all groups for all users. Note that by default a newly created account is assigned to the group EVERYONE. To get more information about groups go to the the section called “Security”.

[fleXive] implements an access control list based approach to security combined with roles. Since handling access permissions on a per user basis would result in tremendous amounts of data, [fleXive] checks permissions based on access control lists on a per user group basis. User accounts can be assigned to any number of user groups and if in rare cases an explicit user based security is needed, the use of one group for each user is recommended.

User accounts are stored in the database and consist basically of the login name together with a hashed password of the assigned groups and roles. For more information about accounts see the section called “User Management”.

Mandators are - contrary to divisions - not transparent to the user but a means of separating accounts and data. Data can be shared between mandators by assigning user groups from multiple mandators to access control lists. Transfer of data between mandators is a planned upcoming feature of [fleXive]. An example when mandators should be used is e.g. to model different departments of a larger company.

Access control lists - which are assigned to user groups - define a list (Read, Edit, Create, etc.) of permissions attached to an arbitrary object like content instances, types, properties (and property assignments) or select lists. See this article on Wikipedia for more information about access control lists. For the purpose of easier organization and logical grouping [fleXive] categorizes ACL's into the following groups:

  • Instance: Attached to content instances.

  • Structure: Attached to types, properties and property assignments.

  • Workflow: Attached to workflow steps.

  • Briefcase: Attached to briefcases, to allow sharing them among different users.

  • Selectlist: Attached to select lists, the only relevant permission is Create to allow users to create new items for that list.

  • Selectlist Item: Attached to selectlist items, to restrict visibility.

Each access control list allows setting the following permissions independently: Read, Edit, Create, Delete, Relate and Export.

Roles can be assigned individually to users and groups. Every user (or account) is eligible to use any role assigned to the user or any group he belongs to. It is considered best practice to assign roles to groups and only in very rare cases (e.g. flagging someone as a global supervisor) should the role assignments be done on an individual (per user) basis.

[fleXive] provides the following roles:

  • GlobalSupervisor: no restrictions at all.

  • MandatorSupervisor: may do everything for "his" mandator

  • ACLManagement: create/update/delete ACL's for "his" mandator

  • AccountManagement: create/update/delete users, groups and roles (may only add roles he is assigned himself, may not alter assigned roles that he has not assigned himself) for "his" mandator. Everybody may read user and group informations (of course no passwords which are hashed anyways), but only for his own mandators unless he is a global supervisor.

  • SelectListEditor: may see the user interface to edit selectlist items (role entitles to no CRUD rights!), actual permissions are taken from the select lists createItemACL

  • WorkflowManagement: create/update/delete steps and workflows for "his" mandator

  • StructureManagement: create/update/delete types, relations, groups, properties, assignments and selectlists and assign scripts to structures for "his" mandator

  • ScriptManagement: create/update/delete scripts

  • ScriptExecution: execute scripts that can be run "standalone" (i.e. not triggered by events)

  • BackendAccess: may login to the administration GUI (does not imply any rights)

One of the core tasks of [fleXive] is to store and query data. Like in relational databases or well-formed XML files, data needs to match a predefined structure. When [fleXive] was designed the requirements were as follows:

  • Simplicity: a very simple, intuitive and slim API
  • Hierarchical: the possibility to group properties together and nest them within subgroups
  • Reusability: if you define how a postal address looks like, you want to be able to reuse that definition
  • Inheritance: inheritance works like reusing a type to refine it
  • Multilingualism: every supported datatype should be able to store different data for different languages
  • Versioning: a user should have the possibility to create a new version of an instance
  • Security: if enabled, access control lists should restrict access to the type, properties, workflow steps and/or certain instances
  • Workflow: a content instance should always be assigned to a workflow step like "edit" or "live". Controlled by access control lists users may change the state following defined routes between states
  • Scripting: At predefined trigger points (like before or after creating a new data instance) scripts can be executed that have the possibility to alter data of the affected instance or perform arbitrary actions like sending emails

A good analogy to explain how structures in [fleXive] are organised are classes and object instances: a class in an object oriented programming language describes which attributes (in [fleXive] called properties) are available. If an attribute itself acts like a container for other attributes we call it a group. We call the class analogon Type (implemented in the class FxType), attributes property (implemented in FxProperty) and a collection of attributes Group (implemented in FxGroup).

To enable reuse of properties and groups these entities are independent of types and need to be assigned to types (and respectively groups). The benefit of this system - although it might sound a bit confusing at first glance - is that different types and assignments can share the same property which can be a big advantage for query operations [1] .

A special extension to types are relations (implemented in FxType, mode: Relation) which mimics the behaviour of attributed n:m relations known from SQL.

Structure elements can be addressed using XPath-like expressions as shown in the section called “Content Engine”.

Table 6.2. [fleXive] structure elements
Element Class Description
Type FxType A type, identified by its name, defines behaviour (what kind of permissions may be applied, storage model or language mode is to be used, etc.) and structure (properties and groups are assigned to types).
Relation FxType (mode:Relation) A relation is basically a type that relates (or links) two other types together. A good analogy is an attributed relation known from SQL. It is possible to define how many times a specific instance may be used as a relation source or destination and which types may be related.
Property FxProperty A property defines a name and a datatype (and some options). Thats all there is to it! It can only exist (and is of relevance) if it is assigned to a type or group. The purpose of keeping properties and their assignments separate is the ability to share them and query across multiple types with a single property.
Group FxGroup A group serves as a container for properties and combines them to an entity. Groups - just like properties can not exist without assignments to types ( FxGroupAssignment). The purpose for their existance is like for properties: the ability to share and query across multiple types.
Assignment FxAssignment An assignment is the correlation of groups and properties to types. A group or property can only be used in instances if it is connected to a group assignment or a type.
Property assignment FxPropertyAssignment The assignment of a property to a group assignment or a type. If the property that is being assigned permits, settings like the access control list or options may be overridden.
Group assignment FxGroupAssignment The assignment of a group to a group assignment or a type. If the group that is being assigned permits, settings like the access control list or options may be overridden.

Following the convention on how to update or create new instances of classes in [fleXive], for every class exists (or should exist ;-) ) an editable class. These editable classes can either be instantiated with ClassName.createNew(..) or classInstance.asEditable() depending on if you want to create a new instance or edit an existing.

A type, identified by its name, defines behaviour (what kind of permissions may be applied, storage model or language mode is to be used, etc.) and structure (properties and groups are assigned to types).

The following is a list of parameters that can be passed as arguments to FxTypeEdit when editing or creating a new type:

Table 6.3. FxTypeEdit parameters
Parameter Method Create Edit Description
ACL setACL(ACL acl) X X The ACL which is checked when new instances of this type are created. Will only be checked if the type is configured to check permissions.
Default instance ACL setDefaultInstanceACL(ACL defaultInstanceACL) X X Sets the default instance ACL which is assigned to newly created content instances of that type per default.
Category setCategory(TypeCategory category) X X A type can be assigned the categories User orSystem. A User categorized type can be edited by anyone with proper roles (StructureManagement), whereas System categorized types are ment to be [fleXive] internal and only to be changed by users with the role GlobalSupervisor. The mode may only be changed by users with the role GlobalSupervisor (under ordinary circumstances changing the category of a type should never be necessary).
Check validity setCheckValidity(boolean checkValidity) X X If set totrue, content instances can be assigned a validFrom and validUntil Date. FxContent instances provide anisValid(long time)-Method to check if they are valid at the requested time. This feature is particularly useful in queries since only valid content instances will be returned. Using this feature allows for instance time triggered publication (and removal) of articles.
Label setLabel(String label) X X Set a label for the type. Of relevance only to user interfaces.
Enable parent assignments setEnableParentAssignments(boolean enableParentAssignments) X - If a type is derived from another type, this flag decides if the derived assignments should be enabled. This is by default enabled when creating a derived type: FxTypeEdit.createNew(String name, FxString label, ACL acl, FxType parent) and can be disabled using this method before the derived type is saved.
History age setHistoryAge(long historyAge) X X If trackHistory is enabled for this type, the historyAge determines the duration for which history entries exist. All entries older than this time (in milliseconds) will be removed. History entries are changes to the type or instances and are not fully implemented yet.
Language mode setLanguage(LanguageMode language) X (X) Set one of the supported language modes:
  • None: Content is not language dependent
  • Single: One language per content may be defined
  • Multiple: Every property may exist in different languages
The language mode of existing types can only be changed if no content instances exist.
Maximum destination count (Relation) setMaxRelDestination(int maxRelDestination) (X) (X) Restrict the total number of instances that may be related to a source instance using this relation type. The value 0 means unlimited. This value can only be set if the type is a relation and no instances would invalidate this restriction.
Maximum source count (Relation) setMaxRelSource(int maxRelSource) (X) (X) Restrict the total number of instances that may be related to a destination instance using this relation type. The value 0 means unlimited. This value can only be set if the type is a relation and no instances would invalidate this restriction.
Maximum versions setMaxVersions(long maxVersions) X X Set the max. number of instance versions to keep, if negative unlimited, 0 does not keep any versions.
Automatic versioning setAutoVersion(boolean autoVersion) X X If set to true a new version is automatically created if data (and not only positioning) has changed when saving a content.
Mode setMode(TypeMode mode) X (X) Set if this type is to be used as a regular type or as a relation. Changing the mode is currently only allowed if no content instance exist. Possible modes are:
  • Content: The "regular" type
  • Relation: Type used as relation
Name setName(String name) X X The name of the type. Has to be unique.
Permissions setPermissions(byte permissions) X X Set the permissions to check. The parameter contains the bitcoded types of permissions that should be checked. Please use the setUseXXXPermission()-convenience methods, where XXX is Type, Property, Step or Instance. If you want to use bit coded permissions, use these constants:
  • FxPermissionUtils.PERM_MASK_TYPE
  • FxPermissionUtils.PERM_MASK_PROPERTY
  • FxPermissionUtils.PERM_MASK_STEP
  • FxPermissionUtils.PERM_MASK_INSTANCE
Remove instances with relation types setRemoveInstancesWithRelationTypes(boolean removeInstancesWithRelationTypes) - (X) Only applies to relations: If relation entries are removed (for instance, you no longer want to relate Type A and B using this relation), all instances of this relation type that relate the removed FxTypeRelation will also be removed.
State setState(TypeState state) X X Changing the state allows to (de-)activate a type.
  • Available
  • Locked - temporary unavailabilty, should only be set from [fleXive] internally to allow for timeconsuming processing.
  • Unvailable
Track history setTrackHistory(boolean trackHistory) X X Enables history tracking (will log changes to the type itself or instances). How long history entries are kept can be set with setHistoryAge(long)
Use instance permissions setUseInstancePermissions(boolean use) X X Should instance permissions be checked? If enabled, the ACL assigned to instances will be checked.
Use property permissions setUsePropertyPermissions(boolean use) X X Should property (assignment) permissions be checked? If enabled, the ACL assigned to property assignments will be checked. Property permission checks are disabled by default and should only be used if really necessary (i.e. if you want to hide specific company or department internal properties from users that under normal circumstances should be allowed to read the instance).
Use step permissions setUseStepPermissions(boolean use) X X Should step permissions be checked? If enabled, the ACL assigned to workflow steps will be checked. Enable these checks if you want to prevent users from seeing/using instances in certain steps (i.e. an editor should not be allowed to edit already published articles - this is something only users from the quality assurance department should be allowed to do).
Use type permissions setUseTypePermissions(boolean use) X X Should type permissions be checked? If enabled, the ACL assigned to this type will be checked. Disabling this check, will allow everyone to create or remove instances of this type (if instance permission checks are enabled, they will of course still be checked in the case of a removal).
Allow multiple instance ACLs setMultipleContentACLs(boolean value) X X If true, multiple content instance ACLs are allowed for content instances of this type.
Workflow setWorkflow(Workflow workflow) X (X) Assign a workflow to this type. For existing types the workflow can only be changed if no instances exist.

In the following example we create a new type "Customer", provide a multilingual label and assign an access control list:


Another way to create a new type would be to use the GroovyTypeBuilder:


Every data type can potentially support values for different languages - depending on the properties' and property assignments' multilingual support settings.

Table 6.4. [fleXive] data types
Data type FxValue class Description
HTML FxHTML HTML markup. Unlimited in length. Offers the ability to use HTML editors in user interfaces.
String1024 FxString A String with a maximum length of 1024 characters. Use this data type in favor of Text if you don't need more than 1024 characters, since usually Text is stored in CLOB Database columns while String1024 uses VARCHAR columns.
Text FxString Like String1024 but unlimited in length.
Number FxNumber Numerical type corresponding to the Java Integer class.
LargeNumber FxLargeNumber Numerical type corresponding to the Java Long class.
Float FxFloat Numerical type corresponding to the Java Float class.
Double FxDouble Numerical type corresponding to the Java Double class.
Date FxDate A date corresponding to the Java Date class. Note that there is no time information saved!
DateTime FxDate A date corresponding to the Java Date class. This data type stores date and time information.
DateRange FxDateRange A date range corresponding to two Java Date class instances with a start- and enddate. Note that there is no time information saved!
DateTimeRange FxDateTimeRange A date range corresponding to two Java Date class instances with a start- and enddate. Time information is saved as well using this data type.
Boolean FxBoolean A boolean value corresponding to the Java Boolean class.
Binary FxBinary This data type stores information about a binary content and allows streaming of that content. For more information please refer to the section called “Handling binary content”
Reference FxReference A reference to a content instance, identified by its primary key (FxPK).
InlineReference - This data type is only planned but not implemented. It will allow to create contents that do not exist on their own (and can not be loaded on their own or queried for) but embedded in another content instance. Their XPath addressing will be relative to their parent group in the embedding content.
SelectOne FxSelectOne This data type allows the selection of one entry of a SelectList. See the section called “Select lists” for further information about select lists.
SelectMany FxSelectMany This data type allows the selection of many entries of a SelectList. See the section called “Select lists” for further information about select lists.

Working with binary content is like working with ordinary input- and outputstreams in Java. For uploading a binary an InputStream and for downloading an OutputStream has to be passed. Transfer of binaries is handled by [fleXive]'s own streaming framework fxStream. It uses nonblocking tcp sockets as transport medium if the client and server part exist in different virtual machines and is able to detect if they run within the same VM for optimal performance.


Select lists usually contain a set of related select items, which are used in GUIs to enable the user to make a selection of one or more of these items. [fleXive] allows in-memory creation of slim select lists for quick GUI display, as well as defining and persisting fully fledged deeply nested select lists. Names of selectlist items have to be unique within their respective list.

While only users in the role of SelectListEditor may create, update and delete select lists, select list items are handled differently. Whether a specific user is permitted to add itmes to and remove items from a specific select list is handled by the createItem ACL of the select list. Whether users may read, edit, select and deselect a specific select list item is handled by the ACL of the select item itsself. The operations and relevant permissions are specified in Table 6.7, “Relevant permissions for working with select list items”.

Table 6.7. Relevant permissions for working with select list items
Operation ACL Permission Description
Creating a select list item FxSelectList.createItemACL CREATE The createItemACL of the item's select list controls which users may create and hence add select list items to this specific select list.
Deleting a select list item FxSelectList.createItemACL DELETE Similar to the creation of new select list items, the createItemACL of the item's select list also controls which users may remove select list items from this specific select list.
Editing a select list item FxSelectListItem.acl EDIT Whether a user may change the data of a specific select list item is controlled by the EDIT permission of the ACL of the select list item itsself.
Reading a select list item FxSelectListItem.acl READ Whether a user may read a specific select list item and its data (for exmaple in the content editor) is controlled by the READ permission of the select list item.
Adding a select list item to a selection (when working with contents) FxSelectListItem.acl READ, CREATE Whether a user may add specific select list item to his seleciton is controlled by the READ (otherwise the user wouldn't see that the item exists) and by the CREATE permission of the select list item.
Removing a select list item to a selection (when working with contents) FxSelectListItem.acl READ, DELETE Whether a user may remove specific select list item from his seleciton is controlled by the READ (otherwise the user wouldn't see that the item exists) and by the DELETE permission of the select list item.

A property defines a name and a datatype (and some options). It can only exist (and is of relevance) if it is assigned to a type or group. The purpose of keeping properties and their assignments separate is the ability to share them and query across multiple types with a single property.

There are two ways to assign a property to an existing type or group: Either by calling FxPropertyEdit.createNew(..) method which can clone existing or create entirely new properties or by reusing an existing property assignment by calling FxPropertyAssignmentEdit.createNew(..).

Example 6.7. Creating new properties

1        AssignmentEngine assignmentEngine = EJBLookup.getAssignmentEngine();
2        ACL customerACL = CacheAdmin.getEnvironment().getACL(ACLCategory.STRUCTURE.getDefaultId());

3        FxPropertyEdit name = FxPropertyEdit.createNew("Name",
4                        new FxString("Name of the person"),
5                        new FxString("Enter the persons name"),
6                        FxMultiplicity.MULT_1_1,
7                        customerACL,
8                        FxDataType.String1024);
9        assignmentEngine.createProperty(typeId, name.setAutoUniquePropertyName(true), "/");

1

Obtain a reference to the assignment engine bean which is responsible for manipulating assignments

2

Get the default ACL used for structures

3

Create a new property with the alias "Name"

4

Label

5

Hint for user interfaces

6

Configure this property to be required (See the section called “Multiplicity” for more information)

7

Assign the ACL to be used. This ACL will only be checked if the type this property will be assigned to has property permission checks enabled

8

The data type will be a String with a maximum length of 1024 characters. See the section called “Data types” for an overview of available data types.

9

Since properties can not exist if they are not assigned to a type, we assign them to the type with the id typeId to the root group ("/" - for an example how to assign it to a group of choice seethe section called “Groups and group assignments”). Property names have to be unique but since "Name" is very likely to be used by another property already we allow [fleXive] to auto-generate a unique property name (in the form of "propertyname_"+running number) and make use of the feature that most setters return the object itself (in this case the FxPropertyEdit object) which we pass to the createProperty(..)-method. This method will create the property and assign it to the type we passed as first argument - creating a new property and property-assignment entry.


To reuse an existing property-assignment is even simpler:


One of the most important concepts of [fleXive] is the relation between properties (or groups) and their assignments to types (or groups). As mentioned in the introduction to this chapter: "A property defines a name and a datatype (and some options). It can only exist (and is of relevance) if it is assigned to a type or group. The purpose of keeping properties and their assignments separate is the ability to share them and query across multiple types with a single property."

Essentially this means, that while a property does have unique attributes (which can be overwritten), its assignments to different types imply that these attributes can differ from the "original". As soon as a property has no more assignments (i.e. is no longer associated to a type), it is removed from [fleXive] The ability to further assign the same property to multiple types enables [fleXive] to carry out queries for these properties and returning results from any type they are attached to.

A simple, theoretical example: Given the types "A" and "B", and property "T" having its label set to "foo". First, "T" is assigned to "A" Second, "T" is assigned to "B" and the assignment's label set to "bar". Step I.) Querying "T"'s label (i.e. loading the FxProperty) will yield "foo". Step II.) Querying for "T"'s assignment to "B" will yield "bar". Step III.) After deleting the assignment of "T" to "A", the query in Step I.) will still yield "foo".

This concept is best explained by giving a comprehensive example using actual [fleXive] code: The types "Person" and "Anotherperson" are created. The property firstname will first be assigned to "Person", then an assignment of the same property is made to "Anotherperson". The original property assignment is then removed from "Person" showing that the property will still retain the attributes it received during creation.

Please note that the concepts explained here also apply to groups and their assignments.

Example 6.9. Property assignment concepts

import com.flexive.shared.scripting.groovy.*
import com.flexive.shared.value.*
import com.flexive.shared.security.*
import com.flexive.shared.*
import com.flexive.shared.structure.*

new GroovyTypeBuilder().person(label: new FxString("Person"),                     1
        acl: CacheAdmin.environment.getACL(ACLCategory.STRUCTURE.defaultId),
        multilang: true) {
}
// we "reload" the "Person" type to show the means of adding properties using the
// GroovyTypeBuilder after type creation.
def builder = new GroovyTypeBuilder("Person")                                           2
builder {
    firstname(dataType: FxDataType.String1024,                                          3
            multilang: false,
            label: new FxString(FxLanguage.ENGLISH, "A person's first name"),     4
            multiplicity: FxMultiplicity.MULT_0_N)
}

new GroovyTypeBuilder().anotherperson(label: new FxString("Anotherperson"),       5
        acl: CacheAdmin.environment.getACL(ACLCategory.STRUCTURE.defaultId),
        multilang: true) {
            anotherfirstname(assignment: "PERSON/FIRSTNAME",                            6
            label: new FxString(FxLanguage.ENGLISH, "Another's first name"))      7
}

def name = ""
def type = CacheAdmin.getEnvironment().getType("person")
FxPropertyAssignment propAssign = type.getPropertyAssignment("PERSON/FIRSTNAME")
name = propAssign.getLabel()
EJBLookup.getAssignmentEngine().removeAssignment(propAss.getId(), false, false);        8


FxProperty prop = CacheAdmin.getEnvironment().getProperty("firstname");                 9
return "Removed assigned (label): " + name + "; Retained label for property: "
    + String.valueOf(prop.getLabel())                                                   10

1

The type "Person" is created.

2

The GroovyTypeBuilder is called again loading the just created type. Incidentally this also shows how to create properties for a type using the builder after type creation.

3

Here we create / assign the property "firstname" and ..

4

.. set its label to "A person's first name".

Note that the GroovyTypeBuilder both creates an FxProperty as well as assigns it to the given type within the same block of code.

5

The type "Anotherperson" is created ..

6

.. and the firstname property is assigned to this type having the alias "anotherfirstname".

7

The label is set to "Another's first name".

8

After temporarily storing the property's label (for later reference) we remove the assignment of the property "firstname" from the type "Person".

9

Since only the assignment was removed, we can still load the "original" property.

10

We can then return both the assignment's label which was just removed, and for comparison reasons the label of the "stand-alone" property, showing they will be the same. The script will return the text: Removed assigned (label): A person's name; Retained label for property: A person's name


The following is a list of parameters that can be passed as arguments to FxPropertyEdit and FxPropertyAssignmentEdit when editing or creating a new propery/assignment:

Table 6.8. FxPropertyEdit/FxPropertyAssignmentEdit shared parameters
Parameter Method Create Edit Description
ACL setACL(ACL acl) X X The ACL which is checked when the type using this property or assignment is configured to check property permissions. Setting this value for FxPropertyAssignmentEdit is only allowed if the referenced property allows to override its ACL.
Hint setHint(FxString hint) X X Set a hint text to be displayed in user interfaces.
In overview setInOverview(boolean inOverview) X X Display in overviews. This is an option for user interfaces and a convenience method to set an option (See the section called “Property and group options”). The constant FxStructureOption.OPTION_SHOW_OVERVIEW ("SHOW.OVERVIEW") is used. The property can restrict this setting from being overwritten in an assignment.
Label setLabel(FxString label) X X Set a label text to be displayed in user interfaces.
Multilingual setMultiLang(boolean multiLang) X X Allow multilingual values for this property. This is a convenience method to set an option (See the section called “Property and group options”). The constant FxStructureOption.OPTION_MULTILANG ("MULTILANG") is used. The property can restrict this setting from being overwritten in an assignment.
Multiline setMultiLine(boolean multiLine) X X A hint for user interfaces if this property should be rendered using input elements with multiple lines. Useful for String/Text based properties (See the section called “Data types”) which should be displayed in a textarea instead of a single input field. Decision how to render the component is up to the user interface. This is a convenience method to set an option (See the section called “Property and group options”). The constant FxStructureOption.OPTION_MULTILINE ("MULTILINE") is used. The property can restrict this setting from being overwritten in an assignment.
Multiplicity setMultiplicity(FxMultiplicity multiplicity) X (X) Set the multiplicity of this property. Can only be changed if no instances exist that would violate the new setting. (See the section called “Multiplicity” for more information). The property can restrict this setting from being overwritten in an assignment.
Options setOption(..) setOptionOverridable(..) clearOption(..) X X Option related operations. Seethe section called “Property and group options”.
Searchable setSearchable(boolean searchable) X X Allow user interfaces to use this propery/assignment in visual query editors. This is a convenience method to set an option (See the section called “Property and group options”). The constant FxStructureOption.OPTION_SEARCHABLE ("SEARCHABLE") is used.
Use HTML editor setUseHTMLEditor(boolean useHTMLEditor) X X Hint for user interfaces to use a HTML editor when editing values of this property. Only makes sense for String/Text based data types. This is a convenience method to set an option (See the section called “Property and group options”). The constant FxStructureOption.OPTION_HTML_EDITOR ("HTML.EDITOR") is used.

Table 6.9. FxPropertyEdit exclusive parameters
Parameter Method Create Edit Description
Auto unique property name setAutoUniquePropertyName(boolean autoUniquePropertyName) X - Property names have to be unique to allow querying them. Setting this option to true will automatically choose a name that has not been used for a propery by adding an underscore and a running number to property names until it is unique. Set this option only if you do not plan on "sharing" a property between different types or dont need to query based on properties but rather property assignments.
Data type setDataType(FxDataType dataType) X - Set the data type of this property. Please see the section called “Data types” for more information.
Fulltext indexed setFulltextIndexed(boolean fulltextIndexed) X X Enable fulltext indexing and queries for a property. See the section called “Fulltext search” for more information.
Name setName(String name) X - Set the name of this property. This name is used in assignments as a proposal for the XPath alias (unless a different one is requested). Currently the name can not be changed for existing properties. To query across assignments using the same property, this name is used.
Overridable ACL setOverrideACL(boolean overrideACL) X X Restrict if assignments may use an ACL different from the one defined for the property. If set to false an assignment may still set an ACL but the ACL of the property is used and the assignments is ignored.
Overridable HTML editor setOverrideHTMLEditor(boolean overrideHTMLEditor) X X Restrict assignment to override the HTML editor option (User interface hint). This is a convenience method to set an option (See the section called “Property and group options”). The constant FxStructureOption.OPTION_HTML_EDITOR ("HTML.EDITOR") is used.
Overridable Multilinguality setOverrideMultiLang(boolean overrideMultiLang) X X Restrict assignment to override the multilinguality option (allow multilingual values). This is a convenience method to set an option (See the section called “Property and group options”). The constant FxStructureOption.OPTION_MULTILANG ("MULTILANG") is used.
Overridable Multiline setOverrideMultiLine(boolean overrideMultiLine) X X Restrict assignment to override the multiline option (User interface hint). This is a convenience method to set an option (See the section called “Property and group options”). The constant FxStructureOption.OPTION_MULTILINE ("MULTILINE") is used.
Overridable multiplicity setOverrideMultiplicity(boolean overrideMultiplicity) X X Restrict if assignments may override the multiplicity of this property. (See the section called “Multiplicity” for more information).
Overridable Overview setOverrideOverview(boolean overrideOverview) X X Restrict assignment to override the overview option (User interface hint). This is a convenience method to set an option (See the section called “Property and group options”). The constant FxStructureOption.OPTION_SHOW_OVERVIEW ("SHOW.OVERVIEW") is used.
Overridable Searchable setOverrideSearchable(boolean overrideSearchable) X X Restrict assignment to override the searchable option (User interface hint). This is a convenience method to set an option (See the section called “Property and group options”). The constant FxStructureOption.OPTION_SEARCHABLE ("SEARCHABLE") is used.
Referenced list setReferencedList(FxSelectList referencedList) X (X) If the properties data type is SelectOne or SelectMany (See the section called “Data types” for more information) the referenced selectlist (See the section called “Select lists”) can be assigned. Updating an existing property is only allowed if no data instances using the original selectlist exist.
Referenced type setReferencedType(FxType referencedType) X (X) If the properties data type is Reference (See the section called “Data types” for more information) the referenced type (See the section called “Types”) can be assigned. Updating an existing property is only allowed if no data instances using the original type exist.
Unique mode setUniqueMode(UniqueMode uniqueMode) X (X) Set the uniqueness level of this property. May only be changed if no content instances using this property exist. (See the section called “Uniqueness of values” for more information).

Table 6.10. FxPropertyAssignmentEdit exclusive parameters
Parameter Method Create Edit Description
Alias setAlias(String alias) X - Set the alias of a property assignment. Property assignments may define an alias to allow multiple use of the same property but using a different name. The alias is the rightmost part of the XPath used to address an assignment. Changing an alias for existing assignments is not supported (yet).
Default language setDefaultLanguage(int language) X X Defining a (optional) default language preselects this language in multilingual values as the default language.
Default multiplicity setDefaultMultiplicity(int defaultMultiplicity) X X The default multiplicity determines how many values will be initialized for an assignment. Useful in user interfaces to pre-create a set of entries instead of one.
Enabled setEnabled(boolean enabled) X (X) Enables or disables a property assignment - making it unavailable to editors, etc. Updating an existing assignment is currently experimental and might have side-effects ...
Parent group assignment setParentGroupAssignment(FxGroupAssignment parent) X - If this assignment is assigned to a group, the assignment of the parent group (in the context of the current type)
Position setPosition(int position) X X Set the position of the assignment (within the same parent group). Changing an assignment's position will be update all other affected assignments within the same group. Invalid values will be adjusted (to 0 or the max. possible position)

A group basically only defines a name (and some options). It can only exist (and is of relevance) if it is assigned to a type or another group. The purpose of keeping groups and their assignments separate is the ability to share them and be consistent with how properties and property assignments are handled.

Just like using properties, there are two ways to assign a group to an existing type or another group: Either by calling FxGroupEdit.createNew(..) method which can clone existing or create entirely new groups or by reusing an existing group assignment by calling FxGroupAssignmentEdit.createNew(..).

Example 6.10. Creating a new group

1        AssignmentEngine assignmentEngine = EJBLookup.getAssignmentEngine();

2        assignmentEngine.createGroup(
3                typeId,
4                FxGroupEdit.createNew(
5                        "Address",
6                        new FxString("The customers address"),
7                        new FxString("Enter the customers address here"),
8                        true,
9                        FxMultiplicity.MULT_1_1).
10                        setAssignmentGroupMode(GroupMode.AnyOf),
11                "/");

12        FxPropertyEdit street = FxPropertyEdit.createNew(...);
13        FxPropertyEdit zip = FxPropertyEdit.createNew(...);
14        assignmentEngine.createProperty(typeId, street, "/Address");
15        assignmentEngine.createProperty(typeId, zip, "/Address");

1

Obtain a reference to the assignment engine which is needed to create groups, properties and (of course) assignments

2

We're about to create a new group

3

Since groups can not be created without assigning them to a type, we have to provide the id of the type we want to assign this group to.

4

The second parameter to AssignmentEngine.createNew(..) is a new FxGroupEdit instance.

5

"Address" is the name we chose for the new group

6

The label (for user interfaces)

7

The hint (for user interfaces again)

8

This parameter allows overriding the multiplicity assigned in the next line by assignments to the group

9

We make the group required, setting the multiplictiy to 1..1 (See the section called “Multiplicity” for more information)

10

This parameter is optional, since it is the default value. We set the group mode to allow any of the group's children to be present. An alternative would be OneOf where only one child of the group may be set. (See the section called “Group modes” for more information)

11

We need to provide the XPath relative to the root group of the type where we want to assign the group to. Using "/" will assign it directly to the root group.

12

We create a property called "Street" which we want to assign to the address group later.

13

Same for the property "ZIP"

14

The "Street" property is created like in the section called “Properties and property assignments”, but we assign it to our new created group "/Address"

15

And again for the "ZIP" code


Here's another example for creating groups using the GroovyTypeBuilder and for attaching content using the GroovyContentBuilder:

Example 6.11. Creating a new group using the GroovyTypeBuilder

        import com.flexive.shared.scripting.groovy.*
        import com.flexive.shared.value.*
        import com.flexive.shared.security.*
        import com.flexive.shared.*
        import com.flexive.shared.structure.*

1      new GroovyTypeBuilder().person(label: new FxString("Person"),
2                acl: CacheAdmin.environment.getACL(ACLCategory.STRUCTURE.defaultId),
3                multilang: true) {
4                    firstname(dataType: FxDataType.String1024,
                        multilang: false,
                        label: new FxString(FxLanguage.ENGLISH, "First name"),
                        multiplicity: FxMultiplicity.MULT_0_N)

5                    lastname(assignment: "PERSON/FIRSTNAME",
                       label: new FxString(FxLanguage.ENGLISH, "Last name"),
                       hint: new FxString(FxLanguage.ENGLISH, "Last name required"),
                       multiplicity: FxMultiplicity.MULT_1_N)

6                    ADDRESS(label: new FxString(FxLanguage.ENGLISH, "Address"),
                        multiplicity: FxMultiplicity.MULT_0_N) {

7                         street(dataType: FxDataType.String1024,
                             label: new FxString(FxLanguage.ENGLISH, "Street (Nr)"),
                             multiplicity: FxMultiplicity.MULT_0_N)
                     }
        }

8      def builder = new GroovyContentBuilder("PERSON")

9      builder {
10           firstname("John")
11           lastname("Doe")
12           address {
13               street(new FxString(false, "Ameaningfulstreetname 444"))
            }
         }
14       EJBLookup.getContentEngine().save(builder.getContent())

1

Create the type "Person" using the GroovyTypeBuilder ..

2

.. use the default ACLs ..

3

.. and set the type to support multiple languages.

4

Create the first property "firstname".

5

Another property for the last name (implicitly required because auf the default multiplicity of 1..N), which is a derived assignment from "FIRSTNAME".

6

Here, the group "Address" having the XPath "ADDRESS" is created. Important: UPPERCASE LETTERS (Either the whole group name or simply the first letter, as in "Address") always denote the creation of a Group.

7

The group's only property: "street".

8

Retrieve a GroovyContentBuilder instance.

9

Call the builder passing the parameters (XPathname([contentValue]):

10

Set the firstname to "John".

11

Set the lastname to "Doe".

12

"Open up" the group's XPath value using "address".

13

Pass the value "Ameaningfulstreetname 444" to the XPath ADDRESS/STREET

14

Save the content by retrieving the ContentEngine EJB and calling its save() method.


The following is a list of parameters that can be passed as arguments to FxGroupEdit and FxGroupyAssignmentEdit when editing or creating a new group/assignment:

Table 6.11. FxGroupEdit/FxGroupAssignmentEdit shared parameters
Parameter Method Create Edit Description
Assignment GroupMode setAssignmentGroupMode(GroupMode mode) X (X) Set the group mode to allow any of its possible children or just one to be present. Can only be changed for existing groups/assignments if no instances exist (See the section called “Group modes” for more information)
Hint setHint(FxString hint) X X Set a hint text to be displayed in user interfaces.
Label setLabel(FxString label) X X Set a label text to be displayed in user interfaces.
Multiplicity setMultiplicity(FxMultiplicity multiplicity) X (X) Set the multiplicity of this group. Can only be changed if no instances exist that would violate the new setting. (See the section called “Multiplicity” for more information). The group can restrict this setting from being overwritten in an assignment.
Options setOption(..) setOptionOverridable(..) clearOption(..) X X Option related operations. Seethe section called “Property and group options”.

Table 6.12. FxGroupEdit exclusive parameters
Parameter Method Create Edit Description
Name setName(String name) X - Set the name of this group. This name is used in assignments as a proposal for the XPath alias (unless a different one is requested). Currently the name can not be changed for an existing group.
Overridable multiplicity setOverrideMultiplicity(boolean overrideMultiplicity) X X Restrict if assignments may override the multiplicity of this group. (See the section called “Multiplicity” for more information).

Table 6.13. FxGroupAssignmentEdit exclusive parameters
Parameter Method Create Edit Description
Alias setAlias(String alias) X - Set the alias of a group assignment. Group assignments may define an alias to allow multiple use of the same group but using a different name. The alias is part of the XPath and is used to address an assignment. Changing an alias for existing assignments is not supported (yet).
Default multiplicity setDefaultMultiplicity(int defaultMultiplicity) X X The default multiplicity determines how many values will be initialized for an assignment (i.e. how many groups are created upon initialization). Useful in user interfaces to pre-create a set of entries instead of one.
Enabled setEnabled(boolean enabled) X (X) Enables or disables a group assignment - making it unavailable to editors, etc. Updating an existing assignment is currently experimental and might have side-effects ...
Parent group assignment setParentGroupAssignment(FxGroupAssignment parent) X - If this assignment is assigned to a group, the assignment of the parent group (in the context of the current type)
Position setPosition(int position) X X Set the position of the assignment (within the same parent group). Changing an assignments position will be upate all affected other assignments within the same group. Invalid values will be adjusted (to 0 or the max. possible position)

The GroovyTypeBuilder ("GTB") provides a simple means for structure creation in [fleXive] using the Groovy scripting language. The GTB is based on Groovy's builder support, enabling us to easily create and manipulate [fleXive]'s hierarchical structures. Here is what the GTB can do (for you):

  • Create a structure: types, derived types, properties & groups and their respective assignments
  • Change a structure (1): Assignment attribute changes (if allowed or overridable)
  • Change a structure (2): Create additional assignments within a given type, "walk through" an existing structure

Structural or assignment changes pertain to any element within an existing type, with the exception of types themselves (please refer to the examples in the section called “GroovyTypeBuilder Usage Examples” for further reference).

A simple example of the GTB can be seen below. We create the type "PERSON" having one attribute "NAME":


Based on Groovy, a familiarity with its syntax is an asset, but not a requirement to understand the very simple syntax rules for the GTB.

General rules:

  • Properties have to start with a lower-case letter
  • Groups have to start with an upper-case letter
  • (Empty) Groups can either be denoted w/o parentheses (incl. curly braces), as in
    Groupname {
    }
                        
    or have parentheses w/o curly braces, as in
    Groupname ()
                        
  • Attributes for types, properties, groups and their respective assignments have to be inside the parentheses following the element's name. Attributes are separated by commas ",".
    propertyname (label: new FxString("Property label"), multilang: false)
                        

Creation and change of structural elements

The following is a list of all available attributes for the GroovyTypeBuilder. The parameters correspond to the type/property/group parameters mentioned in the section called “Structure Engine” Parameters / attributes are split up into the following categories:

  • Type attributes: these apply to types only
  • Property & group common attributes: these apply both to the creation of new properties and to groups
  • Property attributes: these apply to the creation of new properties
  • Group attributes: these apply to the creation of new groups
  • Property & group assignment common attributes: these apply to property group assignments and changes thereof
  • Property assignment attributes: these apply to property assignments or changes thereof
  • Group assignment attributes: these apply to group assignments or changes thereof

As mentioned inthe section called “GroovyTypeBuilder Syntax”, all attributes are called via the following syntax:

[STRUCTURENAME] ([attribute]: [value] [, ..])

E.g: firstname(multiLang: false, label: new FxString(true, "First name"))

Warning

Attributes are CASE-SENSITIVE.

Attributes in the wrong case will be ignored, misspelled attributes *might* be interpreted as generic structure options.

Override options which are not explicitly set will by default return "true". The following overrides are affected: "overrideMultiplicity", "overrideACL", "overrideSearchable", "overrideMultiline", "overrideInOverview", "overrideMultilang", "overrideMaxLength", "overrideUseHtmlEditor"

Guarantee: The GTB guarantees that changes to assignments will only affect the attributes parameterised in the call to the GTB. (i.e. a change to the "label" has no effect on any other attributes, an "empty" call to an existing assignment does not affect the assignment at all).

Hint: Examples might omit part of the code.

Table 6.15. Type Attributes
Attribute Input Example Description
acl String or ACL testtype(acl: "Default Structure ACL") or testtype(acl: CacheAdmin.getEnvironment().getACL(ACLCategory.STRUCTURE.getDefaultId()) Sets the access control list for the given type. If not given, the "Default Structure ACL" is used
generalACL String or ACL new GroovyTypeBuilder().testtype(acl: "Default Structure ACL", generalACL: "Custom ACL") { prop1() prop2(acl: "Default Structure ACL") } Sets a general (ACLCategory.STRUCTURE) ACL which is valid for all (newly) created property assignments within the type. A given "acl" will override the "generalACL"
defaultInstanceACL String or ACL Providing an instance ACL named "Foobar Instance ACL" exists: new GroovyTypeBuilder().testtype(defaultInstanceACL: "Foobar Instance ACL") { ... } Sets a default content instance ACL for the given typ
label FxString testtype(label: new FxString(true, "Property 01")) Sets the type's label. If not given, the element's name is used (code example: "testtype"
parentTypeName String testtype(parentTypeName: "ROOT") Creates a derived type from the given parentTypeName.
languageMode LanguageMode testtype(languageMode: LanguageMode.Single Determines how the type handles languages
typeMode TypeMode testtype(typeMode: TypeMode.Relation) Sets the mode of an FxType
trackHistory Boolean testtype(trackHistory: true) Enables history tracking for a type.
historyAge Long testtype(trackHistory: true, historyAge: 10L) Sets the number of history entries. Only used in conjunction with "trackHistory"
maxVersions Long testtype(maxVersions: 5L) Sets the number of versions to keep.
autoVersion Boolean testtype(autoVersion: true) Automatically create a new version when saving and data (not positions only!) has changed.
useInstancePermissions Boolean testtype(useInstancePermissions: true) Use instance permissions
usePropertyPermissions Boolean testtype(usePropertyPermissions: true) Use property permissions
useStepPermissions Boolean testtype(useStepPermissions: true) Use step permissions
useTypePermissions Boolean testtype(useTypePermissions: true) Use type permissions
usePermissions Boolean testtype(usePermissions: true) Use all permissions if set to "true"
workflow String or Workflow testtype(workflow: "Editor Workflow") Set the given Workflow for the type (Workflow must exist)
[GENERIC STRUCTURE OPTION] String new GroovyTypeBuilder().ford(COLOUR:"black") Set a generic option for the given type
[GENERIC STRUCTURE OPTIONS] as a List: "structureOptions" List<FxStructureOption>

def myOptions = new GroovyOptionBuilder().opt1(value: true) { opt2(value: "An option value", isInherited: false) }

testtype(structureOptions: myOptions)

Set the type's options via a List of FxStructureOptions. The structureoptions can, for instance, be created using the GroovyOptionBuilder (refer to JavaDoc f. instructions)
icon FxReference testtype(icon: new FxReference([example omitted for brevity]...)) Set an icon for the given type
Table 6.16. Properties and Groups - Common Attributes (Property / Group Creation)
Attribute Input Example Description
label FxString prop(label: new FxString(true, "Property 01")) Sets the property's label. If not given, the element's name is used (code example: "prop")
name String testprop(name: "myprop") Will create a property named "MYPROP". W/o the "name" attribute, the property's name would be "TESTPROP" Set the property's or group's name. The name attribute will override any name given during structure creation.
alias String address(alias: "address_a") (will create the XPath assignment "TYPENAME/ADDRESS_A" Set the alias for a given property or group. Will affect the XPath!
hint FxString Address(hint: new FxString(true, "Contains address properties")) Set the property's or group's hint attribute
multiplicity FxMultiplicity or String phonenumber(multiplicity: "0,3") Set the property's or group's multiplicity (cardinality). A default of 0..1 is assumed
defaultMultiplicity Integer Address(defaultMultiplicity: 0) Set a property's or group's default multiplicity. A default of 1 is assumed
overrideMultiplicity Boolean street(overrideMultiplicity: false) Set whether the property's or group's assignment may override the base multiplicity. A default of "true" is assumed for properties, "false" for groups.
[GENERIC STRUCTURE OPTION] String Address(label: new FxString("Address Group") "BUSINESS": true) or Address(label: new FxString(" Address Group") BUSINESS: "Telecom provider") Set a generic option for the given property
[GENERIC STRUCTURE OPTIONS] as a List: "structureOptions" List<FxStructureOption>

def myPropOptions = new GroovyOptionBuilder().foo(value: "bar") { foobar(value: "barfoo", overridable: false) barfoo(value: "foobar", overridable: false, isInherited: false) }

testtype(structureOptions: myPropOptions)

Set the group's or property's options via a List of FxStructureOptions.
Table 6.17. Exclusive property attributes (property creation)
Attribute Input Example Description
acl String or ACL street(acl: "Default Structure ACL") or street(acl: CacheAdmin.getEnvironment().getACL(ACLCategory.STRUCTURE.getDefaultId()) Sets the ACL for the given property. By default the "Default Structure ACL" is used.
overrideACL Boolean street(acl: "Default Structure ACL", overrideACL: true) The property's assignments may override the property's ACL
dataType FxDataType htmlEdit(dataType: FxDataType.HTML) Set the datatype for the given property. If not given, FxDataType.String1024 is assumed
autoUniquePropertyName Boolean name(autoUniquePropertyName: false) Automatically creates a unique property name should the same property (name) exist already. By default the attribute is "true"
fullTextIndexed Boolean note(dataType: FxDataType.Text, fullTextIndexed: false) Sets the full text search attribute for properties
multilang Boolean title(dataType: FxDataType.String1024, multilang: true) Sets the property's multilanguage attribute
overrideMultilang Boolean title(multilang: false, overrideMultilang: true) Determines if the multilang option may be overriden in assignments
overrideInOverview Boolean title(overrideInOverview: false) Assignments may override the "INOVERVIEW" option
maxLength Integer phonenumber(maxLength: 25, dataType: FxDataType.Text) Restrict the maximum length of input values. Works in conjunction with FxDataType.String1024 and FxDataType.Text
overrideMaxLength Boolean title(overrideMaxLength: false) Assignments may override the "MAXLENGTH" option.
overrideMultiline Boolean title(overrideMultiline: false) Assignments may override the "MULTILINE" option
overrideSearchable Boolean title(overrideSearchable: false) Assignments may override the "SEARCHABLE" option
overrideUseHtmlEditor Boolean htmledit(overrideUseHtmlEditor: false) Assignments may override the html editor option
searchable Boolean htmledit(searchable: false) Property can be searched
inOverview Boolean htmledit(inOverview: true) Property is in overview
useHtmlEditor Boolean numberarea(useHtmlEditor: true, dataType: FxDataType.Number) Property uses the html editor
multiline Boolean numberarea(multiline: true, dataType: FxDataType.HTML) Use a multiline input field for entering values
defaultValue FxValue title(dataType: FxDataType.String1024, defaultValue: new FxString(false, "Dr.")) Sets the default FxValue for the given property.
uniqueMode UniqueMode note(FxDataType.HTML, uniqueMode: UniqueMode.Global) Set the uniqueness of a property
referencedType FxType or String imageref(dataType: FxDataType.Reference, referencedType: "IMAGE") or imageref(dataType: FxDataType.Reference, referencedType: CacheAdmin.getEnvironment().getType("IMAGE") Sets the referenced FxType for a property
referencedList FxSelectList or String country(dataType: FxDataType.SelectOne, referencedList: "COUNTRIES" or country(dataType: FxDataType.SelectOne, referencedList: CacheAdmin.getEnvironment().getSelectList("COUNTRIES") Sets the FxSelectList for a FxDataType.SelectOne / .SelectMany property

As is the case with all assignments, the attribute settings are taken from the base assignment, any attributes listed here can be used to change them (e.g. the "label") if applicable (or if overridable)

Table 6.19. Property and Group Assignments - common attributes (property / group assignments and changes thereof)
Attribute Input Example Description
assignment String property(assignment: "ROOT/CAPTION") Create an assignment from an existing property or group assignment by specifying the XPath. When creating group assignments, also consider using "createSubAssignments" (see the the section called “Group Assignment Attributes” for further information on this attribute).
label FxString property(label: new FxString(true, "Property 01")) Set or change the assignment's label
defaultMultiplicity Integer Address(defaultMultiplicity: 0) Set a property's or group's default multiplicity IFF overrideBaseMultiplicity = "true"
alias String address(assignment: "CONTACTDATA/ADDRESS", alias: "address_a") Set the alias for a given property or group. Will also change the XPath.
hint FxString Address(hint: new FxString(true, "Contains address properties")) Set the property or group assignment's hint attribute
multiplicity FxMultiplicity or String phonenumber(multiplicity: "0,3") Set the propertys or groups assignment's multiplicity
enabled Boolean alternatePhone(assignment: "CONTACTDATA/ADDRESS/PHONENUMBER", enabled: false) Disable an assignment
[GENERIC STRUCTURE OPTION] String Address(label: new FxString("Address Group") "BUSINESS": true) or Address(label: new FxString(" Address Group") BUSINESS: "Telecom provider") Set a generic option for the given assignment
[GENERIC STRUCTURE OPTIONS] as a List: "structureOptions" List<FxStructureOption>

def myPropOptions = new GroovyOptionBuilder().foo(value: "bar") { foobar(value: "barfoo", overridable: false) barfoo(value: "foobar", overridable: false, isInherited: false) }

testtype(structureOptions: myPropOptions)

Set the group's or property's options via a List of FxStructureOptions.
Table 6.20. Exclusive property assignment attributes (assignments and changes thereof)
Attribute Input Example Description
multilang Boolean title(assinment: "CONTACTDATA/TITLE", multilang: true) Sets the property assignment's multilanguage attribute if overridable
defaultValue FxValue author(assignment: "MYBOOK/AUTHOR", defaultValue: new FxString(false, "Dr.")) Sets the default FxValue for the given assignment.
acl String or ACL street(assignment: "CONTACTDATA/ADDRESS/STREET", acl: "Default Structure ACL") or street(assignment: "CONTACTDATA/ADDRESS/STREET", acl: CacheAdmin.getEnvironment().getACL(ACLCategory.STRUCTURE.getDefaultId()) Sets the ACL for the given assignment.
defaultLanguage Long text(defaultLanguage: FxLanguage.GERMAN Set the assignment's default language
multiline Boolean text(multiline: true, dataType: FxDataType.HTML) Use a multiline input field for entering values
inOverview Boolean htmledit(inOverview: true) Property assignment is in overview
useHtmlEditor Boolean numberarea(useHtmlEditor: true, dataType: FxDataType.Number) Property assignment uses the html editor
maxLength Integer textarea(multiline: true, maxLength: 100) Restrict the maximum length of input values.
searchable Boolean htmledit(searchable: false) Property assignment can be searched
flatten Boolean Example: create a property "someprop" and activate the hierarchical storage mode for its assignment: new GroovyTypeBuilder().aTypeName() { someprop() someprop(flatten:false) } Activates or deactivates the flatstorage for a given property assignment

Several examples of using the GTB can be found throughout the reference documentation. This section further illustrates the various uses the GTB can be put to when creating or updating structural information in [fleXive].

  • Aliases

    Aliases can be assigned during element creation and can also be changed for existing assignments. However, it is not necessary to perform subsequent structure walk-throughs by calling the aliased name (albeit possible).

    Example: Create a type "TEST" having a property "NAME" with the aliased name "firstname". This will lead to an XPATH assignment of "TEST/FIRSTNAME" for the given property assignment.

    new GroovyTypeBuilder().test {
        name(alias: "firstname")
    }
                    

    The label can then be changed by employing

    def builder = new GroovyTypeBuilder("TEST")
    builder {
        name(label: new FxString("First name"))
    }
                    

    Any derived assignments MUST consider the aliased name (or its XPath, respectively):

    def builder = new GroovyTypeBuilder("TEST")
    builder {
        lastname(assignment: "TEST/FIRSTNAME")
    }
                    

    As mentioned above, an alias can also be changed for existing elements:

    def builder = new GroovyTypeBuilder("TEST")
    builder {
        name(alias: "name")
    }
                    

    .. which would alter the XPath of the above property assignment to "TEST/NAME"

  • Aliased assignments from other types

    A word of caution: Calling aliased assignments from other types will not work (this mainly has to do with the way the GTB has to "guess" where it is within a structure and what it is looking for & also due to the way that structure traversal works).

    Example: A structure with an aliased assignment from "ROOT/CAPTION"

    new GroovyTypeBuilder().test {
        caption(assignment: "ROOT/CAPTION", alias: "caption_alias")
    }
                    

    Now we try to call the same structure again:

    def builder = new GroovyTypeBuilder("TEST")
    builder {
        caption()
    }
                    

    This yields a NEW property within "TEST" by the name of "CAPTION"

    Q: How can I go about changing an aliased assignmnent (from another type)?

    A: Use the alias instead of the property's name! Example:

    def builder = new GroovyTypeBuilder("TEST")
    builder {
        caption_alias(label: new FxString("My derived caption"))
    }
                    

  • AutoUniquePropertyName / Alias Caveats

    Due to the inherent "update-feature" of the GTB, it is of utmost importance to understand how the Builder handles the creation of properties having the SAME name within a given structure. This is best explained by the following examples.

    def builder = new GroovyTypeBuilder().test {
        prop1()
        prop1()
    }
                        

    The above example will create "Prop1" having the default attribute values, the second call to prop1() will NEITHER create a new property (unique name: "PROP_1") NOR update the existing one.

    def builder = new GroovyTypeBuilder().test {
        prop1()
        prop1(dataType: FxDataType.Text)
    }
                            

    In the above example, an attempt is made to create a second property with the autoUniquePropertyName "PROP1_1" This, however, will fail since the generated XPATH for the respective property assignment will be equal to the XPath of the original "PROP1" assignment (XPaths provide a UNIQUE way to address assignments, therefore no equal XPaths can exist within the same hierarchical level).

    Hence the correct way to go about this is to use an alias (which will also set the XPath accordingly):

    def builder = new GroovyTypeBuilder().test {
        prop1()
        prop1(dataType: FxDataType.Text, alias: "prop1alias")
    }
                            

    If, on the other hand, the new property (having the same name as an existing one) resides within a different hierarchical level of the given structure, the alias attribute (i.e. setting a different XPath) can be omitted:

    def builder = new GroovyTypeBuilder().test {
        prop1()
        Group1 {
            prop1()
        }
    }
                            

  • Assignment Paths

    When creating assignments from properties / groups, either the full XPath or a relative XPath can be used to denote the base assignment. Where "relative" implies, that type names can be omitted.

    Example 1: full XPath (capitalising the XPath is not necessary)

    def builder = new GroovyTypeBuilder().test {
        prop1()
        Group1 {
            prop1(assignment: "TEST/PROP1")
        }
    }
                            

    Example 2: "relative" XPath

    def builder = new GroovyTypeBuilder().test {
        prop1()
        Group1 {
            prop1(assignment: "/prop1")
        }
    }
                            

The GroovyOptionBuilder ("GOB") provides a means for creating Lists of FxStructureOptions using Groovy. Just like the GTB, the GOB extends Groovy's builder support class. Let us get straight to the examples to demonstrate how the GOB works:

Considerations

  • The GroovyOptionBuilder will always return a List of FxStructureOptions
  • It is recommended to always quote the names of the options, e.g. "MYOPTION"
  • The options "overridable" and "isInherited" are true by default

When the persistence engines (structure and content) of [fleXive] were designed, we tried to solve this problem by using a generic approach (unlike hibernate, which accesses data instances with (auto)generated classes). Since we wanted to use hierarchical data structures which are quite like XML in nature, the weapon of choice was an XPath-like approach: Values are accessed using their XPath.

As a simple example lets consider the following XML file:

<?xml version=“1.0“ standalone=“yes“?>
<Person>
    <Name>Max Muster</Name>
    <Phone>+43 1 12345</Phone>
    <Phone>+43 1 800 FLEXIVE</Phone>
    <Address>
        <Street>Private road</Street>
    </Address>
    <Address>
        <Street>Office lane</Street>
    </Address>
</Person>

With [fleXive] we would create a type called Person. We can use the Java API or a custom Groovy builder, the GroovyTypeBuilder, which resembles the hierarchic structure of the type more closely.



Lets have a look at the following table, which is based on the XML data example, to visualize the mapping of XPath's to values:

Table 6.22. XPath to value mapping
XPath Value
PERSON/NAME[1] Max Muster
PERSON/PHONE[1] +43 1 12345
PERSON/PHONE[2] +43 1 800 FLEXIVE
PERSON/ADDRESS[1]/STREET[1] Private road
PERSON/ADDRESS[2]/STREET[1] Office lane

The XPath starts with the name of the type (which is optional if addressing in a FxContent instance, since there the type if obviously known but is needed for example in query results) and the path to address the property. Please note that XPaths are not case-sensitive and that an index of 1 is optional: PERSON/ADDRESS[2]/STREET[1] for example is identical to PERSON/ADDRESS[2]/STREET.

FxContent serves as a container for groups, properties and general information about a content instance. It is used to initialize (create an empty content instance), create, save and load content instances [2] .


The purpose of this example is to demonstrate how easy it is to create, update and remove content instances. Since a content instance is tied to a type (See the section called “Types” for more information) the first thing that needs to be done is to initialize a new (empty) content instance 1. Initializing a content instance creates as many property or group entries as defined in the respective assignments' default multiplicity. Setting a value to a property assignment 2 is done by creating a new FxValue instance corresponding to the property's data type for the XPath of the assignment. The same applies if a group is involved, as shown in 4 for the street property of the first address group. Adding a new index is done by simply setting the XPath to the desired value like in 3 or 5. Please note that using an index of 3 would be illegal if no index of 2 exists.

Removing an XPath is as simple as calling co.remove("/Address[2]") if the second address group should be removed.

A content is created 6 and updated by calling the ContentEngine's FxPK save(FxContent content) method which returns the primary key of a content instance. Loading is done by providing the primary key to the ContentEngine's load(FxPK pk)-method. 7.

Changing a value can be done by assigning a new FxValue instance (like in 5) or by reading the current value 8, and changing it 9 which is updated in the content instance as can be seen from the println command on the application servers console.

To remove a content instance (10) only the primary key - and all required permissions - is needed.

To be able to identify a content, a primary key (implemented in FxPK) is needed. This primary key consists of the unique identifier (usually equivalent to the id the content is stored in the database with) and the version. Depending on the configuration of the type the content belongs to it is possible to create different versions of a content.

A primary key can consist of a distinct version (a numerical value) or a predefined constant:

  • MAX: Constant to select the maximum (highest) available version

  • LIVE: Constant to select the version whose workflow step is flagged as live

To create a new version simply call the ContentEngine's createNewVersion(FxContent co) method.

Information about all versions of a content can be retrieved using the ContentEngine's FxContentVersionInfo getContentVersionInfo(FxPK id) method. FxContentVersionInfo contains information about the minimum-, maximum- and most recently modified version numbers as well as information which user was the last to update each version and if a live version exists.

[fleXive] contents can be queried using a SQL-like dialect called FxSQL. The core idea of FxSQL is to provide a flat virtual table that contains all content instances visible to the current user, with full support for [fleXive] data types and multilingualism.

While it is possible to submit queries in plain FxSQL, there are alternatives for Java and Groovy developers in the form of query builders. They handle proper formatting of values and are the preferred way of formulating queries unless you need some of the more esoteric features of FxSQL.

The overall query layout is similar to SQL. Currently queries are always on the virtual table "content" that provides a flat view on all content instances.

SELECT @pk, caption           -- 1
FILTER version=live              -- 2
WHERE caption LIKE 'test%'       -- 3
ORDER BY caption                 -- 4

1

We select the virtual property @pk (the content primary key) and the standard property caption.

2

Filters work similar to the SQL HAVING clause by providing a post-processing filter mechanism on the results of the actual query. That is, filters are applied every time the search query is executed, and are not stored in the cached result tables. This means that changing filters usually does not invalidate the query cache, whereas changing the WHERE clause always does. In this example, we might want to select only live versions. If you do not specify a version filter, the search uses the maximum version of contents for matching conditions and selecting properties.

3

The WHERE clause supports most standard SQL operators for string and numerical comparisons. Here we select all contents whose caption starts with "test". Note that string comparisons are case insensitive.

4

To control the sorting of the result set, you can specify one or more columns in the ORDER BY clause. By default an ascending sort is used, to sort by descending values add DESC to the column alias (e.g. ORDER BY id DESC). Note that you can only use columns that are specified in the SELECT clause.

The rest of this section contains an enumeration of FxSQL features along with example queries demonstrating that feature.

When performing a search on a content collection that supports metadata such as briefcases, you can select the metadata of an item using the virtual property @metadata.

SELECT @pk, @metadata FILTER briefcase=21

The @metadata column returns instances of type com.flexive.shared.FxReferenceMetaData<FxPK>.

To obtain the FxLock instance associated to a content, selected the virtual property @lock.

SELECT @pk, @lock

When the content instance is not locked, the @lock column returns null.

In addition to the briefcase filters described in the section called “Filters” [fleXive] 3.2.0 added arbitrary conditions for the contents contained in a briefcase. This can be used to implement nested queries by building "filter briefcases" which are then matched against some reference property, without having to use explicit "IN (...)" conditions (which do have database-dependent limitations on the maximum number of constant elements).

Of course you can select all system properties, like acl or created_by, in your query. Some system properties are linked to their backing table. For example, the acl property is linked to the FXS_ACL table, so you can select any column in that table using a field suffix: acl.description selects the description column, and acl.color returns the color code. The most useful field, however, is the virtual acl.label field that returns the ACL label in the calling user's language.

Another useful example is to select the user name that created a content, instead of the account ID:

SELECT @pk, created_by.username

The following table contains all system properties that are linked to their backing table, including the most important fields. For a complete field list, please refer to the database schema.

Table 6.25. System property fields provided in FxSQL
Property (Table) Fields
acl (FXS_ACL) Selects the content ACL. Additional fields include:
acl.label

The localized label in the calling user's language.

acl.name

The unique name of the ACL.

acl.description

The ACL description.

acl.cat_type

The ACL category.

acl.color

The RGB color code used for this ACL.

created_by, modified_by (FXS_ACCOUNTS) Selects the user that has created/lastly modified the content ( created_by and modified_by both have the same fields). Interesting fields include:
created_by.username

The associated user's username.

created_by.email

The associated user's email address.

created_by.login_name

The unique login name of the user.

mandator (FXS_MANDATOR) Selects the mandator of the content instance. To select the mandator name, use mandator.name.
step (FXS_WF_STEPS) Selects the workflow step of the content instance. You can select the following fields:
step.label

The localized step label in the calling user's language.

step.workflow

The workflow ID of the step (and thus the complete workflow used for this content instance).

step.stepdef

The step definition ID.

step.acl

The ACL of the step.

typedef (FXS_TYPEDEF) Selects the type ID of the content instance.
typedef.name

The unique type name.

typedef.description

The type description in the language selected for the query.

typedef.acl

The type ACL.

typedef.parent

The parent type ID.

typedef.workflow

Returns 1 if workflows are enabled for this type.

typedef.trackhistory

Returns 1 if history tracking is enabled for this type.

To control the sort order of the result set, you can specify one or more columns in the ORDER BY clause. Usually you specify the name of a column previously selected in the SELECT clause, but you can also use the 1-based column index. The direction of the sort (ascending or descending) is set using the ASC and DESC modifiers, respectively. The following two queries order the result by the properties priority and caption (i.e. first the result is sorted by priority, then by caption), the first uses named columns, the second specifies the column indices instead:

SELECT @pk, caption, priority
ORDER BY priority DESC, caption

SELECT @pk, caption, priority
ORDER BY 3 DESC, 2

As described in the section called “Select user-defined columns”, @* expands to whatever columns the user defined for the result content type. This causes a problem when the result should be sorted manually by one of these columns, e.g. because the result table has sortable column headers (as in the result table in the administration GUI): the FxSQL parser has no way to expand @* (because the search has not been submitted yet and thus the content type is unknown), but yet it must be possible to sort by one of these columns. For example, this query does not work:

SELECT @*
ORDER BY caption DESC     -- caption not found in SELECT clause

As a workaround, it is possible to specify otherwise invalid column indices in the ORDER BY clause if @* was selected. Of course this may lead to runtime errors if the user-defined columns are less than the ORDER BY index. In the most probable use case, on-the-fly sorting of a result table, this is not a problem, since the user can only sort by columns that have already been rendered.

For example, the following is a valid FxSQL query but relies on the user having defined at least 3 columns for the result type:

SELECT @*
ORDER BY 3 DESC     -- order by the third column of @*

To ease the pain of having to learn yet another query language, the SqlQueryBuilder provides a thin builder interface for FxSQL queries. The query is created using chained calls to the builder and results in a FxSQL query. For example:

new SqlQueryBuilder()                                   // 1 
     .select("@pk", "created_at", "caption")            // 2
     .type("article")                                   // 3
     .orderBy("created_at", SortDirection.DESCENDING)   // 4
     .getResult();                                      // 5

1

A new query builder is created.

2

We select three columns, the virtual property @pk, the content creation date, and the article caption.

3

We want to search only for contents of type Article.

4

Order the result by creation date, return newest articles first (SortDirection.DESCENDING). You can issue multiple calls to orderBy for sorting by multiple columns. You can also specify the column index instead of the column name. If you specify the property name, you must ensure that this property is selected, otherwise the query builder throws a FxRuntimeException.

5

The getResult() call submits the search query to the search engine EJB and returns a FxResultSet.

A call to SqlQueryBuilder#getQuery() returns the actual FxSQL query, for example, the code snippet above built this FxSQL query:

SELECT @pk, created_at, caption
WHERE (typedef = 'article')
ORDER BY created_at DESC

The basic query condition specifies

  1. a property or assignment whose value will be compared,

  2. a PropertyValueComparator specifying the compare operator (like equals or greater), and

  3. a constant value to compare the content value against, like "5" or "Test". Comparisons between content values are not supported.

SqlQueryBuilder offers an overloaded, general-purpose condition() method for specifying arbitrary conditions, and custom condition methods for tree queries and content type constraints:

condition(...)

Add a condition to the query. This method is overloaded to support both assignment and property queries, for the actual method signatures please refer to the [fleXive] JavaDoc. If you want to apply a function (e.g. YEAR(prop)), you have to use the generic condition(String, ...) methods.

isChild(nodeId)

Adds a tree search condition to the query that limits the search to children (direct and indirect) of the given tree node ID. See the section called “Tree Search”.

isDirectChild(nodeId)

Like isChild, but includes only direct children of the given node.

type(contentType)

Adds a content type constraint to the query, i.e. the expression will match only contents of the given type. Note that this is semantically different from a content type filter, which will be covered in the next section.

When a FxSQL query is submitted to the database, all rows are fetched at once and returned in a FxResultSet object. It contains all result rows within the user-defined limits (unlike the cursor-approach of JDBC), including miscellaneous information like the selected column names, or row count information.

The Briefcase Engine offers collections of content instances that can be shared among multiple users. Briefcase contents can also be queried using the search engine, using a custom filter.

The basic workflow when working with briefcases is as follows:

  • Create a new briefcase using BriefcaseEngine#create . When the ACL parameter is null, the briefcase can only be seen by the user that created it. Otherwise the ACL determines the user groups who can see and edit the briefcase.

  • Use the briefcase ID returned in the previous step to populate the briefcase with content instances using BriefcaseEngine#addItems .

  • You can associate metadata with briefcase items (for example, a flag to indicate some kind of action performed on the briefcase item) in the form of key-value pairs. This information is specific to the briefcase item and does not affect the content instance at all.

    Metadata can be selected from a briefcase in a FxSQL query with the @metadata column. Metadata cannot, however, be queried, i.e. it cannot be part of the WHERE clause. If you need queryable metadata, you should think about using some kind of relation type (i.e. an intermediary type with a reference to the actual content).

  • To access values of the content instances of a briefcase, use the FxSQL interface. For example, this statement selects the primary key and the caption of the items of the briefcase with ID 21:

    SELECT @pk, caption FILTER briefcase=21

    Note that you can also query the content instances inside a briefcase, i.e. you can specify a WHERE clause and further restrict the result set.


[fleXive] comes with a tree component, accessible via the TreeEngine interface with means to create and manage tree based structures and attach FxContent instances to tree nodes. There are actually 2 logically (and physically in terms of used database tables) distinct trees: a live (published) and an edit (preview) tree. There always exists a virtual root node with node Id 1 defined in FxTreeNode.ROOT_NODE . If there is no content manually attached, [fleXive] will create an instance of type Folder and attach it. The same thing applies if a content instance that is attached to a node is removed: it will be replaced by an autogenerated Folder instance. An exception is removal of a content linked to a leaf node. In this case the tree node will be removed as well.

The workflow engine is responsible for creating and maintaining workflows. Unless you need to create your own workflows programmatically, the most important information in this section is the conceptual overview which explains the basic concepts of the workflow engine. The [fleXive] workflow engine is a specialized workflow implementation used by the content engine, it does not offer a generic workflow implementation.

A workflow combines a collection of workflow states and the enumeration of all possible transitions between those states. In [fleXive], the following terminology is used:

Step definition

An abstract definition of a workflow step. It defines a localized label and a unique target.

Unique target

Supports the concept of unique steps for versioned contents. When a workflow step with a unique target is assigned to a content instance version, all other versions of the instance that are in the same workflow step are moved to the unique target step. Since this transition is executed every time a content instance is saved, there can be at most one content instance version in a step with a unique target at any given time.

The canonical example for this is the Edit/Live/Archived workflow for publishing website contents: a content is created in the Edit workflow step. When it is deemed to be ready for publication, the latest version is set to the Live workflow step and is shown on the web page. When an update is necessary, a new version is created in the Edit workflow step which can be modified without altering the Live instance. When the new version is to be published, it is set to the Live workflow step. However, there already exists an old version in the Live step: to resolve this, the Live step has a unique target (e.g. Archived). When the latest version is set to the Live step, the previous Live version is moved to the Archived step.

Workflow

A concrete workflow that consists of steps and routes, including the permission configuration for both.

Step

A concrete assignment of a step definition to a workflow, including an ACL that defines the permissions on objects in this workflow step.

Route

A transition between two steps of a workflow, including the user group for which it should be available.

Although workflows can be edited in the administration GUI, you can also setup new workflows programmatically. The workflow engine is spread across four EJB interfaces, each one handling the create/update/delete operations for its part of the workflow: step definitions, steps, routes, and the workflow objects themselves. We'll show how to create a simple workflow from scratch, for a more elaborate explanation of the methods please refer to the [fleXive] JavaDoc.


Example 6.24. Creating a new workflow with steps and routes

There are EJB interfaces for adding steps and routes to an existing workflow. However, when we create a new workflow we would have to first create an empty workflow, and then create each step and route using EJB calls. To simplify things a bit, the workflow engine can create the steps and routes for a new workflow object using intermediate IDs. An intermediate step or route ID is negative (IDs retrieved from the database are always positive) and can be referenced in routes. Finally we retrieve the created workflow from the environment (workflows are always cached, so there isn't even a method to load workflows directly from the database) and perform some validity checks.

// create a workflow and auto-create steps using intermediate IDs
final Step step1 = new Step(-10, definition1.getId(), ACLCategory.WORKFLOW.getDefaultId());
final Step step2 = new Step(-20, definition2.getId(), ACLCategory.WORKFLOW.getDefaultId());

// create a route between step1 and step2
final Route route = new Route(-1, UserGroup.GROUP_EVERYONE, -10, -20);

// create workflow object and store it in the database
final Workflow workflow = new Workflow(-1, "test wf", "my test workflow",
        Arrays.asList(step1, step2), Arrays.asList(route));
final long workflowId = EJBLookup.getWorkflowEngine().create(workflow);

final Workflow dbWorkflow = CacheAdmin.getEnvironment().getWorkflow(workflowId);
assert dbWorkflow.getRoutes().size() == 1;      // route available?
assert dbWorkflow.getSteps().size() == 2;       // both steps available?

// check from and to steps of our route
assert dbWorkflow.getRoutes().get(0).getFromStepId() == dbWorkflow.getSteps().get(0).getId();
assert dbWorkflow.getRoutes().get(0).getToStepId() == dbWorkflow.getSteps().get(1).getId();

The configuration engine provides a hierarchical storage for configuration properties. It allows to override default parameters with user-specific settings, and uses the distributed cache to avoid database lookups.

A configuration parameter is characterized by the following properties:

A parameter is defined using one of the ParameterFactory methods. The parameter interface uses Java generics to provide typesafe manipulation of the configuration values, for example it is not possible (using the configuration APIs) to store a String value in an Integer parameter. Let's start with declaring a configuration parameter of type Integer:

public static final Parameter<Integer> INT_PARAM =
    ParameterFactory.newInstance(
        Integer.class,       /* value class */
        "/config/test",      /* path */
        ParameterScope.USER,
        "param1"             /* key */,
        21                   /* default value */
    );

Our new parameter has user scope, which (by definition) uses the application configuration as a fallback. This means we can now store values both in the application and user configuration (and because the fallback scopes of application are also used, also in the division configuration). Of course, unless we are logged in as a global supervisor, we cannot update the application configuration, but any user can update his or her own configuration. By default, the configuration engine uses the "least shared" scope, in our case the user scope. The following call puts a value in the configuration of the calling user and displays the new value:

EJBLookup.getConfigurationEngine().put(INT_PARAM, 123456);

System.out.println("User parameter value: " + EJBLookup.getConfigurationEngine().get(INT_PARAM));

Having global supervisor privileges (e.g. in a run-once script) we can also update the fallback value using the division configuration engine (which has the same interface as the configuration engine, but always uses division scope):

EJBLookup.getDivisionConfigurationEngine().put(INT_PARAM, 123456);

The configuration engine supports generic value types through serialization to XML with XStream. The ParameterFactory methods return optimized parameter implementations for most primitive values (e.g. Integer, Long or String), and uses the XStream-based implementation for everything else. For example, the following code declares and uses a parameter for values of type java.awt.Point:

// declare parameter
final Parameter<Point> POINT_PARAM = ParameterFactory.newInstance(Point.class,
                            "/config/test",         /* path */
                            ParameterScope.USER,
                            "pointParam",           /* key */
                            new Point(10, 20)       /* default value */);

// store Point value
EJBLookup.getConfigurationEngine().put(POINT_PARAM, new Point(3, 4));

// retrieve stored value
System.out.println("Point parameter: " + EJBLookup.getConfigurationEngine().get(POINT_PARAM));


[1] Imagine an online-shop scenario with different kinds of articles, each implemented using their own type. Something they have in common is their name. Instead of having to build a query for searching the name property that is an OR-construct including all involved types it would be easier to assign to all types a property called "name" and query this property instead of the assignments.

[2] The term content instance is used to describe all data related to a concrete instance of a FxType where values are assigned to properties. A good analogy would be the instantiation ( FxContent ) of a class ( the section called “Structure Engine” FxType ).

[3] That applies to user interfaces if the tree was loaded from a parent of the inaccessible node. But since any node can act as the root node (which has to be passed when loading the tree) it is possible to see such nodes, if the path from the root node to the desired node is accessible

[fleXive] offers a set of JSF components for working with [fleXive] contents and data structures. This includes components to render and edit FxContent instances, submit search queries and show their results, and create custom content queries. To make your own reusable components, [fleXive] provides a simple-to-use plugin API for wiring your own reusable JSF and EJB components for other [fleXive] applications.

[fleXive] JSF support consists of two major modules:

  • The JSF component library contains a set of JSF components for working with [fleXive] structures in any JSF application

  • The JSF plugin API allows applications or plugins to extend the administration GUI and make themselves extensible.

A comprehensive guide on extending [fleXive] functionality is the section called “Writing reusable [fleXive] components ”.

To use the [fleXive] component library, you have to

  1. Add flexive-plugin-jsf-core.jar to your application's classpath

  2. Add the namespace declaration xmlns:fx="http://www.flexive.com/jsf/core" to the top of your XHTML documents.

  3. On every page using [fleXive] components, you have to add a call to <fx:includes/> somewhere in the HTML head section. This tag includes javascript files and CSS stylesheets needed by the components via Weblets.

  4. If you want to use components based on Yahoo UI, you have to include a call to <fx:yuiSetup/> near the end of the HTML body section. This initializes all YUI components on the current page and loads the required Javascript libraries. You also have to specify the YUI skin class on your page, the most convenient way is through the body tag, e.g.: <body class="yui-skin-sam">.

The [fleXive] components for displaying and editing contents are most crucial for any application that works with [fleXive] contents.

The <fx:content> component offers direct access to FxContent values through the property XPath. It also supports groups and multiplicities, i.e. iteration over properties and groups that may exist more than once in an instance. When no content ID is specified, a FxType name or ID is passed to create a fresh instance of the given property type. Instances can then be saved through the fxContentViewBean, or any other JSF managed bean that has a FxContent property.

A main service of the <fx:content> component is to provide direct access to the property values stored in FxContent instances through JSF-EL expressions. Inside the body of <fx:content var="content">, the following expressions may be used:

#{content.name} , #{content['name']} , #{content['/name']}

Return the FxValue of the property identified by the XPath "/name[1]". For properties like "/name" that are attached directly to the content root node, the notations #{content.name}, #{content['name']}, and #{content['/name']} are equivalent. The following examples won't always specify all possible notations.

#{content.name$label}

Return the assignment label of type FxString for the given XPath.

#{content.name$hint}

Return the assignment hint of type FxString for the given XPath.

#{content['group/text']}

Return the given group property. Note that #{content.group.text} does not work before [fleXive] 3.2.0.

#{content.group}

Return a nested fx:content variable for the given group (since [fleXive] 3.2.0).

#{content.element$list}

Provide a list for all entries of the group (or property) "element". If "element" is a property, the iterator returns FxValue objects, otherwise it returns a list of virtual fx:content instances with a prefix for the current group element ( Example 7.1, “Render FxContent property values” illustrates how this feature can be used for looping over groups with <ui:repeat>).

#{content.element$listAll}

Like #{content.element$list}, but includes empty elements.

#{content.reference$.property}

Resolve the given reference property and provides a content for its properties. An expression can also use several nested resolving operations, e.g. #{content.reference$.owner$.email}.

#{content['group/text$new']}

Create a new instance of "group" and return its text property. Note: the current implementation is a hack and works only for simple cases.

#{content['group/text$mayCreateMore']}

Returns true if evaluating the $new suffix for the given XPath will succeed. Useful when using the $new suffix for assignments with a limited multiplicity, since it could cause an exception otherwise.

#{content.name$valid}

Return true if the given XPath (name) is valid for the content instance.

#{content.name$maxIndex}

Return the maximum index value of the given XPath, i.e. the number of occurrences of the property or group in the XPath.

The Java equivalent of this expressions is FxContent#getMaxIndex(java.lang.String) .

Render-time Components

Be aware that <fx:content> is a render-time component. That is, the content instance is provided during view rendering (i.e. between encodeBegin and encodeEnd), but not during view creation. This means, unfortunately, that it is not possible to use Facelets' compile-time tags like <c:forEach> or <c:if> with EL expressions that contain content instances provided by <fx:content>. Instead, render-time components must be used to achieve similar results - for example, <ui:repeat> to iterate over lists or <ui:fragment rendered="..."> for conditional page rendering.







The <fx:children> component provides an iterator over properties and groups of a content instance. The current value is stored in a variable with the same name as the property. The same can be achieved by using the $list JSF-EL suffix as described in the section called “Accessing property values with JSF-EL”.

The <fx:contentEditor> component allows creation, editing and deletion of content instances similar to the Instance Editor.

Warning

This component depends on <fx:includes htmlEditor="true" yui="true"> on the beginning of the page, together with <fx:yuiSetup/> at the end of the page. Furthermore the component needs to be surrounded by a prefixed multipart form. Beyound the core JSF tag libraries, Ajax4jsf and MyFaces Tomahawk are required as well.

editorId

Id of the content editor component (must be unique in a view, used as key to retrieve a specific content instance stored in view-state).

formPrefix

The form prefix of the surrounding form.

contentId

Id of the content to be edited. The maximum available version is loaded.

pk

FxPk of the content to be edited.

content

Content instance to be edited.

typeId

Type id of the content instance to be created.

type

FxType of the content instance to be created.

valueFormatter

A custom valueFormatter which is passed to <fx:fxValueInput/> components within this component.

reset

True if the content for the given editor id should be reset upon the next request (==removed from content storage and reinitilaized with given attributes, default: false). Normally you won't use this, but if you want to know more, see the section called “Customizing the <fx:contentEditor> component” for details.

reRender

External client id which is to be rerendered upon the next AJAX-request triggered by the content editor component. Normally you won't use this, unless you need to rerender a component containing several fxContentEditors.

editMode

If true initilaizes this component to edit mode, otherwise to view mode (default: false).

disableAcl

True if ACL selectbox should not be rendered (default:false).

disableWorkflow

True if workflow selct box should not be rendered (default:false).

disableEdit

True if "edit" button should not be rendered (default:false).

disableDelete

True if "delete" button should not be rendered (default:false).

disableVersion

True if "save in new version" button and "delete version" button should not be rendered (default:false).

disableCompact

True if "compact" button should not be rendered (default:false).

disableSave

True if "save" button should not be rendered (default:false).

disableCancel

True if "cancel" button should not be rendered (default:false).

disableButtons

True if no buttons should be rendered (default:false).

disableAddAssignment

True if icons for adding/inserting assignments should not be rendered (default:false).

disableRemoveAssignment

True if icons for removing assignments should not be rendered (default:false).

disablePositionAssignment

True if icons for positioning assignments should not be rendered (default:false).

disableMessages

True if the <h:messages/> tag inside the content editor should not be rendered (default:false).

disableReferenceEditor

True if the inline editor controls for references should not be rendered (default: false).

closePanelScript

A JavaScript code which is called when the editor is closed (clicking on the "Save and exit" or "Stop editing" button)

showLockOwner

True if the the respective content's lock owner should be shown (default: true).

hiddenAssignments

A collection of group or property assignment IDs that should be hidden in the editor (default: none).

hiddenProperties

A collection of property IDs that should be hidden in the editor (default: none).

Because of its generic nature the content editor cannot satisfy every need out of the box. However, precautions have been taken to make this component customizable to your specific application needs. Let's look at the different parts that make up the content editor more closely:

Stylesheet

All relevant style entries are prefixed with .fxContentEditor_. So if you want to replace icons and images, change colors etc., you can include your own stylesheet overriding those entries.

Templating

<ui:insert> tags allow you to insert or replace whole parts of the content editor component via <ui:define>.

The provided names of the <ui:insert> tags and respective locations are:

insertTop: located at the very top of the component
contentSettings: replaces the ACL and Workflow select boxes
insertMiddle: located right after contentSettings
buttons: replaces all buttons
editButton: replaces the "Edit" button
createButton: replaces the "Create" button
saveButton: replaces the "Save" button
saveInNewVersionButton: replaces the "Save in new Version" button
deleteButton: replaces the "Delete" button
deleteVersionButton: replaces the "Delete Version" button
compactVersionButton: replaces the "Compact" button
cancelButton: replaces the "Stop Editing" button
saveAndCancelButton: replaces the "Save and Stop Editing" button
additionalButtons: located right after the previous buttons
insertBottom: located at the very bottom of the content editor

Extending functionality

By using templating and FxWrappedContent as described above you can add your own buttons to add additional functionality. Also you may extend existing functionality by calling methods of the component's backing bean FxContentEditorBean from within your own managed beans. See the section called “Content Editor Examples” for examples and important instructions.


Example 7.10. Content Editor adding buttons

In this example three buttons are added, which will be inserted in the contentEditor component's template at the <ui:include/> named "additionalButtons". Note that for the Ajax4jsf button flexive.contentEditor.preSubmit() should be called. If you want to use actions of the component's backing bean, make sure you set up the property action listeners accordingly.


<fx:contentEditor var="myContent" ...>
    <ui:define name="additionalButtons">

        <!-- adding a command button -->
        <h:commandButton value="My Button" action="#{myBean.doSomething}">
            <!-- property action listener for FxWrappedContent-->
            <f:setPropertyActionListener value="#{myContent}" target="#{myBean.content}"/>
        </h:commandButton>

        <!-- adding an ajax command button -->
        <a4j:commandButton value="My Ajax Button" action="#{myBean.doSomething}">
            <!-- necessary js call when using a4j button(caused by a4j multipart form submit bug -->
            onclick="flexive.contentEditor.preSubmit();"
            <!-- rerender attribute -->
            reRender="#{myContent.guiSettings.reRender}"
            <!-- using the content editor component's status -->
            status="#{myContent.editorId}_status">
        </a4j:commandButton>

        <!-- adding property action listeners for the component's backing bean (FxContentEditorBean)-->
        <a4j:commandButton .. action="#{__ceBean.action}">
            <f:setPropertyActionListener value="#{myContent.editorId}" target="#{__ceBean.editorId}"/>
            <f:setPropertyActionListener value="#{myContent.guiSettings.reRender}" target="#{__ceBean.reRender}"/>
        </a4j:commandButton>

    </ui:define>
</fx:contentEditor>


For writing FxContent editors, the <fx:value> component provides a complete property value input row consisting of

The output is controlled by the <fx:formRow> component. All parameters of <fx:formRow> can also be set for <fx:value>, except those that control the actual value being rendered.

Note: <fx:value> is a Facelets compile-time component, this means that its input variables must be bound during view creation. Data exposed by components like <ui:repeat> is not available during "compile time" and cannot be used as input for <fx:value>. Use compile-time iterators such as <c:forEach> instead.

property

The XPath of the property.

var

The value of the "var" attribute of the <fx:content> component whose content should be edited. If nested directly in a fx:content tag, this attribute can be omitted.

label

An optional label that overrides the assignment label.

labelKey

An optional message key that overrides the assignment label.

tooltip

The input label tooltip (optional).

tooltipKey

Message key of the tooltip (overrides tooltip, if not specified and labelKey is set, it defaults to#{labelKey}.tooltip)

readOnly

If true, no input field will be rendered.

forceLineInput

For multiline and HTML properties, force to render a single-line input field (e.g. for search query forms).

inputMapper

An optional input mapper for rendering the input control. For example, properties referencing other content may need a custom input mapper that provides a select list of content instances. For more information take a look at the InputMapper documentation and at Example 7.6, “Use InputMappers for application-specific properties”.

valueFormatter

A FxValueFormatter to be used for rendering the value (in read-only mode only). If not specified, the JSF renderer will choose the appropriate default formatter for the actual value class.

filter

If set to false, HTML output will not be filtered. If set to true (the default), HTML entities will be used for sensitive characters (e.g. "&lt;" instead of "<").

disableMultiLanguage

Disables multi language support even if the FxValue object is multilingual. Only the default translation will be displayed and updated.

disableLytebox

Set this property to true if the Lytebox javascript library used for rendering inline previews of images should not be used.

onchange

An optional javascript expression to be called when an input element changed its value.

autocompleteHandler

An instance of the flexive.yui.AutoCompleteHandler javascript class. This class provides a query method for a client-side autocomplete widget provided by Yahoo UI, as well as a method for formatting the response.

For basic usage examples, look at the the section called “Examples” in the <fx:content> documentation. For examples on controlling the output of the <fx:formRow> component, see the section called “Examples” of that component.

The <fx:fxValueInput> component is a universal component for editing FxValue objects. It supports every datatype of FxValue, for example plain text, numbers, dates, or even binaries. It is used in the administration GUI content and search query editors and in every other place where a FxValue needs to be edited. It also includes multilanguage support by providing a language select list next to the input elements.

The default input language of multi-language values can be set in the user preferences of the administration GUI or programmatically through the user configuration parameter com.flexive.shared.configuration.SystemParameters.USER_DEFAULTINPUTLANGUAGE.

Note that this component is a JSF UIInput component, thus it supports many standard JSF input facilities (like value change listeners or validators).

value

An expression of type FxValue of the value to be edited. Note that the value must not be null, otherwise the input component cannot determine the actual FxValue type.

id

An optional client ID of the input component.

externalId

An optional external ID that overrides the clientId in JSF validation error messages - may be necessary in complex, generic forms, but usually you won't need this.

readOnly

If true, only the value itself will be rendered.

readOnlyShowTranslations

If true, all translations of a property will be shown, else only the best matching translation (in the calling users language or the language marked as default).

forceLineInput

For multiline and HTML properties, force to render a single-line input field (e.g. for search query forms).

inputMapper

An optional input mapper for rendering the input control. For example, properties referencing other content may need a custom input mapper that provides a select list of content instances. For more information take a look at the InputMapper documentation and at Example 7.6, “Use InputMappers for application-specific properties”.

valueFormatter

A FxValueFormatter to be used for rendering the value (in read-only mode only). If not specified, the JSF renderer will choose the appropriate default formatter for the actual value class.

filter

If set to false, HTML output will not be filtered. If set to true (the default), HTML entities will be used for sensitive characters (e.g. "&lt;" instead of "<").

containerDivClass

If this CSS class property is set, the component will be embedded in a div-container using this CSS class as style.

disableMultiLanguage

Disables multi language support even if the FxValue object is multilingual. Only the default translation will be displayed and updated.

disableLytebox

Set this property to true if the Lytebox javascript library used for rendering inline previews of images should not be used.

onchange

An optional javascript expression to be called when an input element changed its value.

accesskey

An optional access key for the input element.

autocompleteHandler

An instance of the flexive.yui.AutoCompleteHandler javascript class. This class provides a query method for a client-side autocomplete widget provided by Yahoo UI, as well as a method for formatting the response.

valueChangeListener

A JSF listener that is invoked when the component value changes.

disableDefaultValidator

When true, the default FxValueInputValidator is disabled (since [fleXive] 3.2.1)


Example 7.13. An autocomplete handler for user names

In this example, we use the predefined JSON/RPC autocomplete provider AutoCompleteProvider#userQuery(String query). It searches for user account names based on the user input and returns possible matches.

<script type="text/javascript">
<!--
    // Initialize the Javascript handler that submits the queries via JSON/RPC and evaluates the response
    var userAcHandler = new flexive.yui.AutoCompleteHandler(function(query) {
        // execute JSON/RPC call and evaluate the response
        return eval("(" + flexive.util.getJsonRpc().AutoCompleteProvider.userQuery(query) + ")");
    });
//-->
</script>

<fx:fxValueInput value="#{backingBean.userName}" autocompleteHandler="userAcHandler"/>

The backing bean property #{backingBean.userName} is a property of type FxString and will hold the submitted user name.



The resultValue tag is a specialized wrapper for the <fx:fxValueInput> to be used for rendering a column value of a search result row. If the value passed is an instance of FxValue, a read-only instance of <fx:fxValueInput> is created which then renders the given value. Otherwise (e.g. for FxPK primary keys or primitive values) a generic formatter is used, which can be customized e.g. for formatting of tree paths with a link formatter.

value

The value to be rendered. Can be of any type.

linkFormatter

A ContentLinkFormatter instance that will be used for rendering links to contents. This applies to the rendering of the virtual properties @pk and @path, as described in the section called “Tree Search”. If not specified, the default ContentLinkFormatter will be used. For a description of the supported link format, please refer to the [fleXive] JavaDoc of this class.

contentLinkFormat

The format string passed to the linkFormatter for content links, i.e. FxPK values. For a description of the supported link format of the default formatter, please refer to the [fleXive] JavaDoc of this class.

itemLinkFormat

The format string used for FxPaths values as returned by the virtual @path property. For a description of the supported link format of the default formatter, please refer to the [fleXive] JavaDoc of this class.

Iterates over a collection of content instances. This component is essentially an iterator wrapping a fx:content component. The content instances can be specified through nested Groovy queries or a SqlQueryBuilder instance.

Renders a thumbnail for a given FxPK or FxBinary reference.

pk

The primary key of the content instance. If the PK is specified, the default thumbnail of the content is rendered.

binary

A specific binary entry of type FxBinary to be rendered. This is useful for for content types with more than one binary property. For an example, see the section called “ <fx:children>.

urlOnly

If true, only the thumbnail URL will be rendered (relative to the context root, e.g. thumbnail/pk5.1/s0).

previewSize

The desired size of the thumbnail. Valid values are taken from BinaryDescriptor.PreviewSizes:

  • PREVIEW1,

  • PREVIEW2 (default),

  • PREVIEW3,

  • SCREENVIEW,

  • ORIGINAL.

forceVersion

If set to max or live, it overrides the version information in the PK or binary descriptor.

includeTimestamp

If true, include a timestamp that forces the thumbnail to be reloaded every time the page is rendered. (since [fleXive] 3.2.0)

Renders a Yahoo UI datatable widget for a [fleXive] result set. This provides an easy way of rendering a result to the user, and it includes a paginator widget.

var

The name of the Javascript variable that exposes the Yahoo UI DataTable widget.

value

The result set to be rendered, or a plain text FxSQL query. The following types are supported:

String

A FxSQL query, for example: SELECT @pk, #image/caption

com.flexive.shared.search.FxResultSet

The result of a FxSQL query.

com.flexive.shared.search.query.SqlQueryBuilder

A FxSQL query builder instance the will be used for fetching the result set.

viewType

The view type of the datatable. "list" renders a common table view for each of the columns, while "thumbnails" renders only the thumbnails as in the backend administration's thumbnail view. Note that the thumbnail view only works if the object's primary key ("@pk") was selected in the query, otherwise no valid thumbnail image can be rendered.

clickHandler

An optional Javascript method for handling clicks on the table with signature function handler(targetEl, pk). The primary key parameter is only available if the @pk column was selected in the FxSQL query.

actionColumnHandler

An optional instance of type flexive.yui.datatable.ActionColumnHandler for generating the contents of the "Action" column - an additional column that can be used for displaying custom actions for contents and that will only be visible when this parameter is set. The flexive.yui.datatable.ActionColumnHandler#getActionColumn method is passed the primary key and the permissions of the column, which can be null if the search result does not contain the @pk or @permissions properties.

previewSize

name of the preview size to be used for thumbnails (see BinaryDescriptor#PreviewSizes ).

rowsPerPage

current value for the paginator (optional).

initialPage

current page index for the paginator (optional).

firstColumn

index of the first column to be included in the table (1-based, optional).

[fleXive] offers a powerful way for mapping web page navigation structures through its tree engine. The components in this section allow rendering of (parts of) the tree in HTML, e.g. web page menus or trees.

Renders part of the content tree as nested <ul> elements. These can be styled via CSS to resemble menus or even pop-up menus, or enhanced with JavaScript using YahooUI components.

var

The variable name where the current tree node of type FxTreeNode will be exposed to the page.

output

The output format.

default

Plain HTML output.

csspopup

Renders additional CSS classes that will show only the first tree level and open the subfolders when the mouse hovers over them.

yui

Renders additional markup that allows the Yahoo UI Menu component to build a menu. If JavaScript is disabled, the plain HTML markup is shown to the use. Note that you have to include YUI on the page with <fx:includes>.

path

The tree folder to be rendered. If none is specified, the root folder ("/") is rendered.

mode

The tree mode, "edit" or "live" (default).

depth

The maximum tree depth for sub folders. For example, a value of 2 will render content of the given tree path and the first level of subfolders.

selectedNodeId

If specified, the given tree node ID will be highlighted in the HTML output.

baseTypeId/baseTypeName

An optional type filter for the rendered nodes. Only nodes whose reference is of the given type (or a subtype of the given type) are rendered.

When a non-folder base type is specified, folders are (by default) not rendered in the response. You can override this by setting the includeFolders attribute.

includeFolders

When baseTypeId/baseTypeName is set, setting this attribute to "true" will include folders and their contents in the output even if the specified base type is not a folder.

includeRoot

When set to true, the root node of the rendered (sub-)tree is included in the output. (default: false)

nodeRenderedMap

An optional map from FxTreeNode to Boolean indicating whether a navigation node should be rendered. Use this for custom node rendering conditions, e.g. based on node contents or naming conventions.

Includes external resources (CSS and JavaScripts) used by [fleXive] components. Place this in the <head> section of your HTML page.

Note that you can include the <fx:includes> tag more than once. This is useful if a component (e.g. the HTML editor) is only needed on some pages and <fx:includes> is already included in the main page template.

Initializes the Yahoo UI widgets on the current page and loads all required libraries. Note that you still have to initialize the YUI bootstrap loader using <fx:includes yui="true">.

When using Yahoo UI you have to specify the skin class on your page, the most convenient way is through the body tag, e.g.: <body class="yui-skin-sam">.

[fleXive] provides a set of JSF managed beans for working with [fleXive] in JSF applications. Some of them are usually used only in administration pages, some will probably be used in every [fleXive] application. For a complete reference consult the [fleXive] JavaDoc.

The fxMessageBean is an application-scoped bean that provides a localized message lookup service for JSF. Instead of a single resource bundle as provided by JSF's loadBundle, the fxMessageBean provides centralized access to the messages of all [fleXive] applications and plugins available in the current context.

While [fleXive] builds on existing JavaEE 5 infrastructure, it integrates additional services that make writing components for [fleXive] applications even easier.

JSF components provide a simple and powerful way of providing additional functionality to existing applications, for example a forum or comment section. A component packages all Java code and web resources into a single JAR file that can be dropped into the lib/ directory of an existing flexive.ear file. After the next [fleXive] startup it is available to all applications contained in the [fleXive] archive.

The [fleXive] distribution contains a build target to create the directory structure of a standalone component, including a build file to compile and package the component.

To create a new component, open the command shell and go to the directory containing the [fleXive] distribution. Execute ant. You are greeted by the welcome screen of the flexive build tool. Enter component.create as the target name and supply the name of your component (e.g. test). When you confirm the input, a new component directory layout is initialized in the distribution's parent directory, in this case in ../test. Your screen should look approximately like this:

Buildfile: build.xml

info:
  [flexive]
  [flexive] Welcome to the flexive build tool. Feel free to use the following build targets:
  [flexive]
  [flexive] project.create
  [flexive]     Creates a new flexive project directory.
  [flexive]
  [flexive] component.create
  [flexive]     Creates a new flexive UI component directory.
  [flexive]
  [flexive] db.create
  [flexive]         Create or reset the database schema of a flexive division.
  [flexive]         Warning: if the schema already exists, it will be dropped (i.e. you will lose all data
  [flexive]         stored in the schema).
  [flexive]
  [flexive] db.config.create
  [flexive]         Create or reset the global flexive configuration schema.
  [flexive]
    [input] Please enter a target name, or quit to exit:
component.create

check:

component.create:
    [input] Name of the component you want to create:
test
  [flexive]
  [flexive] Please confirm your input:
  [flexive] Component name:      test
  [flexive] Base directory:      ../test
  [flexive]
    [input] Are these settings correct? ([y], n)
y
    [mkdir] Created dir: /tmp/test
     [copy] Copying 8 files to /tmp/test
     [copy] Copied 13 empty directories to 6 empty directories under /tmp/test
     [copy] Copying 1 file to /tmp/test
     [echo] Component test created successfully. The component root directory is
     [echo] ../test

                    

Change to your component directory in ../test. The directory structure is similar to a [fleXive] project, the main difference is that there is only one source folder since all classes will be packaged into one JAR file.

.
|-- build.xml
|-- resources
|   |-- META-INF
|   |   |-- faces-config.xml
|   |   |-- flexive-plugins-config.xml
|   |   `-- weblets-config.xml
|   |-- messages
|   |-- scripts
|   |   |-- library
|   |   |-- runonce
|   |   `-- startup
|   |-- templates
|   `-- weblets
|-- src
|   `-- java
`-- web

To check if your build environment is working, execute ant in the component's base directory. An (empty) component JAR file should now be created in dist/test.jar. You can open the component in your favourite IDE now and start adding classes, templates and web pages. If you move the component directory to another directory (e.g. to maintain components in a larger [fleXive] project) be sure to modify the path to the [fleXive] distribution in build.xml.

A JSF component is a JAR file that may contain the following elements:

META-INF/faces-config.xml

Registers JSF beans, components, validators, and navigation rules. Note that you can reference XHTML pages from within your JAR file thanks to the classpath resource resolver as described in the section called “Delivering XHTML pages from the classpath”.

META-INF/*.taglib.xml

Register your own Facelets templates. The templates are stored directly in the JAR file and are referenced relative to the META-INF directory. For example, consider the following JAR contents:

./META-INF/mycomponents.taglib.xml
./templates/button.xhtml

To register your button tag, the mycomponents.taglib.xml might look like the following:

<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
        "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
        "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">

<facelet-taglib>
    <namespace>http://www.mycompany.com/jsf/components</namespace>

    <tag>
        <tag-name>button</tag-name>
        <source>../templates/button.xhtml</source>
    </tag>
</facelet-taglib>

Facelets scans the classpath for *.taglib.xml files and will automatically provide the registered templates and components under the given namespace.

META-INF/flexive-plugins-config.xml

Defines [fleXive] JSF plugins, as described in the section called “The JSF plugin API”.

META-INF/weblets-config.xml

Contains the Weblets configuration if you want to deliver additional web resources like images or stylesheets. The weblets resources are stored in the JAR file like Facelets templates, but their base directory may be any package in the JAR file. For example, the following config file sets the weblets root directory of the [fleXive] components package to com/flexive/faces/weblets and registers the weblet provider for the URI /flexive-web-resources/:

                                        <?xml version="1.0" encoding="UTF-8" ?>
<weblets-config xmlns="http://weblets.dev.java.net/config">
    <weblet>
        <weblet-name>com.flexive.faces.weblets</weblet-name>
        <weblet-class>
            net.java.dev.weblets.packaged.PackagedWeblet
        </weblet-class>
        <weblet-version>@FLEXIVE_VERSION@</weblet-version>
        <init-param>
            <param-name>package</param-name>
            <param-value>
                META-INF.resources.flexive-faces
            </param-value>
        </init-param>
    </weblet>
    <weblet-mapping>
        <weblet-name>com.flexive.faces.weblets</weblet-name>
        <url-pattern>/flexive-web-resources/*</url-pattern>
    </weblet-mapping>
</weblets-config>
                                    

If you attach -SNAPSHOT to the weblet-version number, you can disable browser caching for development builds. Otherwise Weblets will add caching information and you have to change the version number to ensure that all clients will use the most recent version.

One of the main JSF features is its component-centric design. It allows developers to create component packages as JAR files that can be deployed in existing applications with little or no additional configuration. Prominent examples in the free software world are MyFaces Tomahawk, ICEfaces, or JBoss RichFaces.

[fleXive] encourages the use of all JSF and Facelets features for writing reusable components, and adds its own plugin API to the mix for modifying existing [fleXive] applications, most notably the administration GUI. For example, a plugin developer may extend main menu entries, add new buttons or create whole new administration areas inside the administration GUI.

Currently the plugin API is only available in the JSF layer, although it is not specifically tied to JSF. It is a pure Java solution that may be used for adding plugins to any Java application, it's just that inside [fleXive] most sensible extension points for plugins are in the JSF layer.

The plugin API chapter covers only a small set of features needed for extending [fleXive], for information on writing reusable components see the section called “Writing reusable [fleXive] components ”.

Example 7.22. Making a [fleXive] application extensible

The intent of the next two examples is to create an extension point which can handle arbitrary plugins attached to it, and then write a plugin for it. The first part is the responsibility of the original application developer, the second one applies to any plugin developer for that application.

The first step for the application developer is to create a PluginExecutor interface that defines the methods available to plugins. In this example, we want to accumulate numbers generated by plugins. We define the following interface:

public interface TestExecutor extends PluginExecutor {
    void pushResult(int result);
}

The executor takes a result (generated by the plugin), and stores it in an internal data structure. Why do we specify an interface instead of a concrete implementation class? The actual plugin executor implementation depends on the code being extended, and may contain private classes that are not (or should not be) visible to the plugin developer. Also, sometimes it is convenient to implement the executor as an anonymous inner class that has access to the surrounding method's variables, without publishing the actual implementation to somebody else.

Next we declare an extension point, for example in a public shared class:

public static final ExtensionPoint<TestExecutor> EXTENSIONPOINT =
        new ExtensionPoint<TestExecutor>("ourapp.extensionpoint") { };

We bind the extension point to our executor interface, and specify a unique identifier (ourapp.extensionpoint). When a plugin developer does not want to or cannot reference the EXTENSIONPOINT constant in his code, he can create a new extension point with the same identifier (and the same executor interface).

Finally we create the executor implementation:

public class TestExecutorImpl implements TestExecutor {
    private final List<Integer> results = new ArrayList<Integer>();

    public void pushResult(int result) {
        results.add(result);
    }

    public List<Integer> getResults() {
        return results;
    }
}

The class implements the TestExecutor interface, and adds an implementation-specific method to obtain the results. The executor implementation does not have to be visible to the client, but its interface has to.

Extending our application is the last step. We instantiate a new executor, and ask the fxPluginRegistryBean to execute all registered plugins. Then we collect the result and show it to the user.

final TestExecutorImpl exec = new TestExecutorImpl();
PluginRegistryBean.getInstance().execute(EXTENSIONPOINT, exec);
System.out.println("Plugin results: " + exec.getResults());

Take a look at the next example to see how to write a plugin for our application.



The fxPluginRegistryBean is an application-scoped JSF bean that acts as a central registry for all [fleXive] plugins. It is initialized on application startup and scans the classpath for available plugins. An application can retrieve a list of registered plugins for a extension point or execute a plugin executor on all plugins of an extension point.

JBoss Seam is a widespread framework for rapid development of applications with JavaEE 5. It provides a neat wrapper around JSF and EJB that removes most of the "glue code" prevalent in many JSF applications that use EJB. Main features include a close integration of EJB persistence (JPA), a conversation scope for JSF, or business process management via jBPM.

This chapter is a quick guide to integrating [fleXive] into an existing Seam application.

To integrate [fleXive] with Seam, you need to

  1. modify your build file to add the [fleXive] libraries to the packages EAR file, and

  2. modify your web.xml and application.xml deployment descriptors.

Both tasks shouldn't take you more than a few minutes, and both are easy to undo.

First of all, you need unpack a [fleXive] distribution as described in Chapter 4, Writing [fleXive] applications . You don't need to actually start [fleXive], but you need access to the distribution directory.

When you have a local [fleXive] distribution installed, go to you Seam project directory and perform the following tasks:

To test if the [fleXive] integration was sucessful, add the following code snippet to one of your front-end pages:

#{fxSystemBean.buildInfoVerbose}

It should render the [fleXive] version and build number. If no text is rendered, please check the logfile for error messages during the application startup. [fleXive] writes several status log messages on application startup, so you should see at least some messages originating from [fleXive].

When using [fleXive] UI tags on your pages, be sure to declare the fx namespace in your XHTML root element:

xmlns:fx="http://www.flexive.com/jsf/core"

You can use all the [fleXive] JSF and EJB components just like in a stand-alone application. There are, however, some issues where extra thought is required for seamless integration between both frameworks.

Table of Contents

[fleXive] administration GUI basics
Running the administration GUI
Logging in
[fleXive] administration GUI display areas
administration GUI overview
User preferences
The structure editor
The structure editor GUI
Structure tree
Structure tree interaction
View mode vs. edit mode
The type editor
The group editor
The property editor
The options editor
The scripting editor
Saving and deleting
Structure export and import
Structure export GUI
Structure import GUI
The instance editor
Creating contents
Content creation UI elements
Editing contents
Content versioning
Content import and export
Content locking
The content tree
Live and edit tree
The content tree's context menu
The visual query editor
Creating queries with the visual query editor
Query parameters
Query results
Saved queries
Briefcases
[fleXive] administration
User account administration
Creating a user
The user account overview
User group administration
The user group overview
Mandator administration
Creating a mandator
The mandator overview
ACL administration
Creating an ACL
The permission matrix
The ACL overview
Workflow administration
Step definitions
Step definition overview
Creating a workflow
Editing a workflow
The workflow overview
Script administration
Creating a script
The script overview
Run-Once Script Information
The Script Console
Select list administration
Creating a select list
Editing a select list and its select items
Select list overview
Export and Import of Structures and Division
System administration
Search settings
CMIS SQL query
System Configuration
System parameters
Lock administration
History viewer
System information
Language settings
Create random test data for content instances

Provided that you have a working [fleXive] installation, you start the [fleXive] administration GUI by starting your chosen application server. You can access the administration GUI by opening your web browser and entering the address of the administration GUI startpage. By default the address is http://localhost:8080/flexive/adm/main.jsf where localhost can be replaced by the IP address of the application server. The port ( 8080 in the example) depends on your Application Servers settings (8080 is the default for JBoss). After a few moments, you will reach the login page.

If you want to work with the administration GUI as supervisor (a role that is allowed to do everything without any security limitations), the default username to choose is supervisor with its default password supervisor.

Note

When the administration GUI is started for the first time and a user successfully logs in, the execution of run-once scripts is automatically triggered which may take a few seconds. (Simply wait until script execution has completed and press the "Continue" button. The run-once scripts are used to initialize [fleXive]).

The administration GUI consists of four areas:

  • (1) The currently selected administration GUI section: The currently active GUI section is displayed on top, in this case the Content Tree.

  • (2) Currently inactive sections: Inactive GUI sections, are displayed on the bottom. By clicking on an inactive section the section becomes active.

  • (3) The toolbar:

    The toolbar contains the following icons

    • Context sensitive GUI icons: In the left side of the toolbar context sensitive GUI buttons are displayed. If for example the user administration GUI is displayed an icon for creating a user is also available in the toolbar.

    • Plugin icons: Right next to the context sensitive GUI buttons, the icons of the feedback plugin are displayed.

    • Fixed icons: In the right side of the toolbar the fulltext search bar, the icon for accessing user preferences and the logout icon are displayed .

  • (4) The content frame: The content frame is the main display area for the currently opened administration GUI.

The [fleXive] administration GUI provides four main GUIs:

  • Content Tree: The content tree is used for browsing, creating, modifying and deleting content instances.

  • Structures: The structure editors allow the user to create, modify and delete the blueprints of content instances.

  • Search: The query editor allows the user to customize search queries in order to search within the content tree and within content instances.

  • Administration: The administration section provides a collection of GUIs for the administration of [fleXive].

The user preferences GUI can be accessed via the toolbar by clicking the icon. The user preferences GUI provides two tabs. The first tab called "User preferences" allows changing the administration GUI language and the default input language, the e-mail address and the password.

The second tab called "User contact data" allows editing the contact data content instance directly. (For details about editing content instances please refer to the section called “The instance editor”). The contact data content instance is used to store additional user specific data but is actually independent of the actual account data (i.e modifying the e-mail address in the contact data does not forward to the e-mail stored within the account data).

The structure editor allows you to define the structure or blueprints of content instances. In essence the structure of a content instance is defined by three main building blocks:

The smallest building block is represented by property, which defines what data may be assigned to a content instance and who has access to that data. In order to make life easier, a specific set of properties can be grouped together into a group. A group can then be assigned to a different type later on, therefore saving you the trouble of manually assigning every single property to the type all over again. A more detailed explanation can be found in the chapter about structures.

Important

Please note that properties and groups on their own do not "belong to" types and also types do not "consist of" properties. Instead properties and groups need to be assigned to types. In fact the same property can be assigned to multiple types using different property assignments. In theory this means that creating a property and assigning it to a type is a two step process. First you create the property (choose the property name, data type, default value etc.) and secondly the property needs to be assigned to the type using a property assignment. In practice this is automatically handled by the structure editor but it is important to understand that a property is not equal to a property assignment. The property assignment acts as a "link" between the type and the actual property and there are various property settings which can be overridden and fine tuned by the property assignment. Similarly, if you remove a property or group from a type, it means that you really only remove the assignment so that this specific property or group is no longer assigned to the type anymore. Properties and groups that are no longer assigned to another type or another group will be automatically removed from the system.

The structure editor GUI is reachable by clicking the Structure tab (1) in the administration GUI. The GUI is separated into two parts: the structure tree (2) on the left half of the screen and the edit view (3) on the right half.

  • Cut & Paste: Assignments can be cut and pasted in order to

    • change the assignment's position within a type or group by cutting the assignment and pasting it at the desired position.

    • remove a property/group from one type by deleting the according property or group assignment while at the same time assigning the property/group to another type or group.

  • Copy & Paste: Assignments can be copied and pasted in order to

    • assign the selected assignment's property/group to another type or group. I.e. a new property/group assignment for the selected property is created and assigned to the specified type or group at the chosen position. By using copy & paste a property can be used within arbitrary types.

  • Pasting: To facilitate positioning various paste operations are provided:

    • Paste into: This is the default pasting operation for copied assignments. A copy of the selected assignment will be created and pasted into the chosen group or into the chosen type at the first position.

    • Paste into as: Works like "Paste into" but additionally prompts you specify a new alias so you can create a new assignment and paste it into an arbitrary type/group with a different alias.

    • Paste above and paste below: pastes an assignment directly above or below another assignment.

    • Paste above and paste below as: Works like "Paste above" and "Paste below" but additionally prompts you specify a new alias so you can create a new assignment and paste it into an arbitrary type/group with a different alias.

Types are protected by their structure ACL and may additionally be assigned multiple instance ACLs. If the type uses instance permissions it is possible to specify a default instance ACL, which will be used by default whenever a new content instance of this type is created.

By editing the type's permissions you may adapt the permission checks to an arbitrary level of security:

  • Use type permissions: If enabled, the mandatory structure ACL assigned to the type is included in permission checks.

  • Use instance permissions: If enabled, the type also includes its assigned instance ACL(s) when performing permission checks, in addition to its (mandatory) structure ACL.

  • Use property permissions: If enabled, the ACL assigned to the type's properties is also taken into account when a content instance is accessed or modified. Using property permissions it is possible to restrict the access to single properties to arbitrary user groups. (For example imagine a type article with the properties text and comment where only the user group "editors" may change the article text but every user may add a comment).

  • Use step permissions: If enabled, the ACL assigned to the current workflow step of the content instance is included in permission checks.

  • Multiple ACLs per instance: if enabled, multiple instance ACLs can be assigned to an instance of this type.

If you want more information on types and their specific settings please refer to the section called “Type parameters”.

Similar to the group editor, the property editor provides four tabs:

System internal property assignments , together with their properties may not be edited (except by users with the role Supervisor). This is why all input elements are deactivated. It is only allowed to edit their options.

Note

When creating a new property, the "Edit Property Assignment" tab will be hidden (as no property assignment may exist without a property), as well as the options of the property assignment in the "Edit Options" tab. Upon pressing the "Create" button, the property is created and the according property assignment is automatically generated.

Note that property assignments may override specific settings of their property. Whether a specific setting may be overwritten is controlled by the "Override property options" section in the "Edit Property Assignment" tab and by the "Property override options" section in the "Edit Property tab respectively.

If you want more information on properties and their specific settings please refer to the section called “Property parameters”.

Generally speaking an option is just a pair of a key and a value. There are special structure options which trigger a special behavior. If you want more information on built-in options please refer to the section called “Property and group options”. The user may access the options by left clicking the "Edit Options" tab.

Generally assignment options may override property/group options if the overridable flag of the property/group option is enabled. In the image above, the property has four options with the keys "SEARCHABLE", "HTML.EDITOR", "MULTILINE" and "SHOW.OVERVIEW". The "Overridable" checkbox indicates that all options may be overriden by the assignment except HTML.EDITOR.

Options can be added by specifying a key, a value and the overridable flag and clicking the "Add" button which is located in the "Actions" column of the table. An option can be deleted again by selecting the according row and pressing the "Delete" button which is also located in the "Actions" column of the table.

In order to facilitate the editing of options, visual helpers are provided:

  • Option is displayed in green: This is a valid option, for example "SEARCHABLE" in the property and assignment options table.

  • Option displayed with grey background: The assignment tries to override an option which is currently not overridable, i.e "HTML.EDITOR" in the assignment options table.

  • Option displayed in red: This is an invalid option. Either the option has no key, or the option has no value, the key already exists or the assignment tries to override an option with the same value. The key "SHOW.OVERVIEW" exists twice in the assignment options table, therefore making it invalid.

In order to update the visual helpers, the "Validate" button on the bottom left may be clicked.