Copyright © 1999-2010 UCS - unique computing solutions gmbh
Table of Contents
List of Figures
List of Tables
List of Examples
GroovyTypeBuilder
GroovyQueryBuilder
FxResultSet
getRows()
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:
The [fleXive] website will provide you with top-level information about [fleXive]. Have a look at the "Explore [fleXive]" section.
This reference documentation will provide all information to use [fleXive] for the development of your application.
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].
Table of Contents
The Sun Java 6 Platform, Standard Edition Development Kit
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)
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).
Create a new [fleXive] project and install it:
mvn -B archetype:generate -DarchetypeGroupId=com.flexive -DarchetypeArtifactId=flexive-archetype-ear -DarchetypeVersion=0.8 -DarchetypeRepository=http://repo.flexive.org/maven2/ -DartifactId=hello-flexive -DgroupId=test -Dversion=1.0-SNAPSHOT
cd hello-flexive
mvn install
Invoke the Jetty container for the WAR artifact:
cd war
mvn jetty:run
Point your browser to
http://localhost:8080/flexive/
and login with the default credentials
(supervisor/supervisor).
To deploy the EAR to your application server, you need to configure a datasource and possibly install a few libraries. Please follow the guide for your vendor:
JBoss 4.2 (also applies to 5.0+)
Glassfish 2.1 (also applies to 3.0+)
Geronimo 2.1 (Wiki)
WebLogic 10.3 (Wiki)
Create a new [fleXive]-based WAR and install it:
mvn -B archetype:generate -DarchetypeGroupId=com.flexive -DarchetypeArtifactId=flexive-archetype-war -DarchetypeVersion=0.8 -DarchetypeRepository=http://repo.flexive.org/maven2/ -DartifactId=hello-flexive -DgroupId=test -Dversion=1.0-SNAPSHOT
cd hello-flexive
mvn package
Deploy the WAR artifact from the
target
directory to your application server. If you want to use the administration GUI
for managing the repository, download and deploy
flexive-admin.war
from the download page.
For further information on [fleXive] Maven support please continue with the section called “ Building applications with Apache Maven ”.
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.
The following required libraries have to be deployed:
Copy the following file from the
extlib
directory of your [fleXive] binary distribution to
${jboss.home}/server/default/lib:
mysql-connector-java-*.jar
(Alternatively you can download the latest stable
MySQL JDBC
driver)
or
h2.jar
if you are using the H2 database.
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: "&" 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: "&" 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: "&" 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: "&" 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>
<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: "&" 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>
<!--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: "&" 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>
<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: "&" 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: "&" 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: "&" 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&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.
Change to the
flexive-dist
directory of your [fleXive] distribution and run
ant ear. This will create the
flexive.ear
file inside the
flexive-dist
directory. Copy
flexive.ear
to
${jboss.home}/server/default/deploy.
Currently JBoss 4.2.x uses the Java's default maximum heap size,
which means that [fleXive] will probably run out of memory during the
first startup. You have to set the maximum heap size using
-Xmx
to a larger value, at least 256m.
On Linux execute
export JAVA_OPTS="-Xms128m -Xmx512m"
On Windows execute
SET JAVA_OPTS=-Xms128m -Xmx512m
Change to the
${jboss.home}/bin
directory.
On Linux launch JBoss using
sh run.sh -c default -Djboss.bind.address=0.0.0.0
On Windows launch JBoss using
start run.bat -c default -Djboss.bind.address=0.0.0.0
To access the administration GUI point your browser to http://localhost:8080/flexive/adm/
Alternatively you can specify your server's IP address, which is supported by default, too.
The default superuser's username and password for the administration GUI are:
username: supervisor, password: supervisor
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.
Flexive needs several libraries to be deployed to the application server's internal
lib
directories. Execute these steps once glassfish has been setup.
We provide an ant task for you which copies all libraries needed to deploy [fleXive] on Glassfish to the specified directory.
Change to the
flexive-dist
directory of your [fleXive] distribution and run
ant glassfish.libs.
You will be prompted to specify your
${glassfish.home}/domains/domain1/lib/ext
directory.
The
ant
task will copy all required libraries to this directory.
To set up the databases, follow these instructions.
Setting up Glassfish
Go to the target installation directory
java -Xmx256m -jar <<path-to-downloaded-jar>>
extracts the installation JAR to
./glassfish
cd glassfish
On Unix: chmod -R +x lib/ant/bin
Execute the
setup.xml
buildfile
Unix: lib/ant/bin/ant -f setup.xml
Windows: lib\ant\bin\ant -f setup.xml
Starting Glassfish
bin/asadmin start-domain domain1 starts the Glassfish server
http://localhost:4848/ and login using default credentials admin/adminadmin.
Additional information
To stop the Glassfish instance execute ${glassfish.home}/bin/asadmin stop-domain domain1
Admin UI Port: 4848
HTTP Port: 8080
HTTPS Port: 8181
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&characterEncoding=utf8&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&characterEncoding=utf8&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&characterEncoding=utf8&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 →
Change to the
flexive-dist
directory of your [fleXive] distribution and run
ant ear. This will create the
flexive.ear
file inside the
flexive-dist
directory.
Using the autodeploy directory:
Copy the
flexive.ear
to
${glassfish.home}/domains/domain1/autodeploy
Using the Admin UI:
In the Admin Console, go to →
Click
Deploy...
and upload the
flexive.ear
file.
Click
OK
To access the administration GUI point your browser to http://localhost:8080/flexive/adm/ Alternatively you can specify your server's IP address, which is supported by default, too. The default superuser's username and password for the administration GUI are username: supervisor, password: supervisor.
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.
There are several locations to look for information in case trouble strikes:
this section
If there are multiple JBoss installations running in your local network you need
to assign a different partition UDP group to each cluster! This can be done by adding
-Djboss.partition.udpGroup=230.2.3.4
(or any other multicast address you choose thats available)
to the start script/command shown in
Starting [fleXive].
In order to run JBossCache on Linux IPv4 needs to be defined as the preferred IP stack. In the Glassfish admin console, go to → → and add the following option:
-Djava.net.preferIPv4Stack=true
If an ant-task fails, make sure you have Apache ANT 1.7 installed.
If you get the following warning:
Problem: failed to create task or type javacc when executing ant
make sure you installed the ant-optional package.
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:
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
helloworld/src/java
contains the Java sources of the application.
helloworld/resources/scripts
contains [fleXive] scripts that will be used to setup the data structures needed by the application.
helloworld/web
contains the XHTML pages and the web configuration files.
We start with defining the data model of the application. Our blog entry consists of a
posting title and the actual text. To set up the data model when deploying the application,
we create a
run-once script
in the
resources/scripts/runonce/
directory,
hello001.groovy.
A straight-forward way of setting up [fleXive] structures is by using the
GroovyTypeBuilder
for [fleXive] types, using the Groovy scripting language.
import com.flexive.shared.scripting.groovy.* import com.flexive.shared.value.* import com.flexive.shared.structure.* new GroovyTypeBuilder().blogEntry(label: new FxString(true, "Blog Entry"), usePermissions: false) { //caption(assignment: "ROOT/CAPTION") entryTitle(multiplicity: FxMultiplicity.MULT_1_1, label: new FxString(true, "Title")) //
entryText(multiplicity: FxMultiplicity.MULT_1_1, label: new FxString(true, "Text"), multiline: true) //
}
|
The method call blogEntry(...) will create the [fleXive] type "BlogEntry" (type names are not case sensitive). For the first tutorial, we will disable [fleXive] security completely for this type by setting usePermissions to false. | |
|
Create a new property assignment for the blog entry type that is referenced with the path "/entryTitle". The multiplicity of 1..1 indicates a mandatory field, the label will be shown in the input forms. | |
|
The text property is created similar to the title, but we add a "multiline" option since we don't want to enter our blog text in a single line. |
The next step is to define a JSF bean that provides the blog postings
to be shown on the front page of our application. We create a managed bean under
src/java/war,
which will be automatically compiled and packaged into the WAR archive.
import com.flexive.faces.model.FxResultSetDataModel; import com.flexive.shared.exceptions.FxApplicationException; import com.flexive.shared.search.FxResultSet; import com.flexive.shared.search.SortDirection; import com.flexive.shared.search.query.SqlQueryBuilder; import javax.faces.model.DataModel; public class HelloWorldBean { private DataModel blogEntries; public DataModel getBlogEntries() throws FxApplicationException { if (blogEntries == null) { final FxResultSet result = new SqlQueryBuilder() .select("@pk", "entryTitle", "entryText", "created_at") //.type("blogEntry") .orderBy("created_at", SortDirection.DESCENDING) //
.getResult(); blogEntries = new FxResultSetDataModel(result); } return blogEntries; } }
|
We choose the columns to be selected for the result set:
| |
|
To display the newest postings first, we order by descending creation date
( |
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>
In the
resources/META-INF
directory you find the configuration files for the (web) application. Since the provided
web.xml
already uses sensible defaults for running [fleXive] applications, all you need to do
is register your JSF bean in
resources/META-INF/faces-config.xml
and add navigation rules between the overview and the create page:
<managed-bean> <managed-bean-name>helloWorldBean</managed-bean-name> <managed-bean-class>HelloWorldBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> <!-- A navigation rule for our input mask, if the content is saved successfully, "success" is returned, which leads to a direct rendering and response of the index.xhtml view. The entries made via the input mask are shown in this view. --> <navigation-rule> <from-view-id>/create.xhtml</from-view-id> <navigation-case> <display-name>success</display-name> <from-outcome>success</from-outcome> <to-view-id>/index.xhtml</to-view-id> </navigation-case> </navigation-rule>
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.
You can find the datastructures of the hello-world application in the administration GUI's structure editor. Log on to the administration GUI and expand the tab named "Structure". flexive/adm/ You should be able to see the type Blog Entry in the tree. Expand it and click on one of the properties. In the "Edit Property Assignment" tab in the right frame you can see the properties originally set in the hello001.groovy script.

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:
@pk,
document01/file,
caption, and
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.
Below each document object, we want to provide a link that opens the object in the
edit form. We could either pass the primary key selected in the first column through
a bean property, or pass
the content instance itself through the fxContentViewBean and supply it to the
<fx:content>
component.
JSF's
setPropertyActionListener
is a convenient way of setting bean properties when a command link or button is triggered.
The Edit link looks like this:
<h:commandLink action="edit" styleClass="editButton"> <f:setPropertyActionListener target="#{fxContentViewBean.content}" value="#{fxSystemBean.content[row[0]]}"/> Edit... </h:commandLink>
#{fxSystemBean.content[row[0]]}
returns the
FxContent
instance for the given primary key. Note that the JSF listener will only be fired
when the user actually clicks on the commandLink, thus the rather expensive load operation
will only be performed if the user clicks on the edit link.
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>
Like in the previous tutorial, we add a simple JSF managed bean that provides our search results.
package com.flexive.examples.tutorial01;
import com.flexive.faces.model.FxResultSetDataModel;
import com.flexive.shared.exceptions.FxApplicationException;
import com.flexive.shared.search.FxResultSet;
import com.flexive.shared.search.SortDirection;
import com.flexive.shared.search.query.SqlQueryBuilder;
import javax.faces.model.DataModel;
/**
* JSF managed bean for the tutorial01 application.
*
* @author Daniel Lichtenberger (daniel.lichtenberger@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
* @version $Rev: 469 $
*/
public class Tutorial01Bean {
private DataModel documents;
public DataModel getDocuments() throws FxApplicationException {
if (documents == null) {
final FxResultSet result = new SqlQueryBuilder()
.select("@pk", "document01/file", "caption", "created_at")
.type("document01")
.orderBy("created_at", SortDirection.DESCENDING)
.getResult();
documents = new FxResultSetDataModel(result);
}
return documents;
}
}
To serve the image URLs rendered by
<fx:resultValue>
to the browser, we need a servlet providing those images. For this purpose, you have to
add the [fleXive] thumbnail servlet to your
web.xml.
<servlet> <servlet-name>Thumbnail</servlet-name> <servlet-class>com.flexive.war.servlet.ThumbnailServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Thumbnail</servlet-name> <url-pattern>/thumbnail/*</url-pattern> </servlet-mapping>
The [fleXive] template project's
web.xml
already includes this mapping.
Finally, we have to register the data provider bean and provide navigation routes for
our buttons and links in the
faces-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE faces-config
PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
"http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
<faces-config>
<!-- Facelets View Handler Definition. Never remove this one. -->
<application>
<view-handler>
com.sun.facelets.FaceletViewHandler
</view-handler>
</application>
<!-- Define our managed bean -->
<managed-bean>
<managed-bean-name>tutorialBean</managed-bean-name>
<managed-bean-class>com.flexive.examples.tutorial01.Tutorial01Bean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<!-- Return to overview page if upload was successful -->
<navigation-rule>
<from-view-id>/upload.xhtml</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/index.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<!-- Open editor from index page -->
<navigation-rule>
<from-view-id>/index.xhtml</from-view-id>
<navigation-case>
<from-outcome>edit</from-outcome>
<to-view-id>/upload.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
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.
Table of Contents
There are two convenient ways to start a new [fleXive] project:
to use the [fleXive] distribution and create new projects with Apache Ant, or
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
JDK 6 or higher, and
Apache Ant 1.7.0 or higher.
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.
Please go to → → → →
Click
and enter
flexive
for the library name. Press
Select the created library entry, and press the button.
Go to your [fleXive] distribution directory, and select all JAR files
in the
lib/
folder (you can use shift-select here).
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.
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:
Open → + →
Select the project root directory.
Press Eclipse should now open the project in the workspace. To build the project EAR file using Ant, execute →
To check if the project and library has been loaded correctly, try to open
the
EJBExampleBean
class using
→ (
Shift+Ctrl+T
)
Eclipse should display the source code without errors.
We start with creating a new project for the
flexive-test
application directory.
Open →
Select and press
Choose your [fleXive] project directory in Project file location.
Click
Uncheck Create module and click
Let IDEA open the project. Next we add the template module file in the IDEA project settings dialog:
Add a new module by clicking on the "+" button at the top of the screen.
Choose
Import existing module
and select the IDEA module file
(flexive-test.iml) in your project directory.
Press Finish.
Please also note that if you want to work with the sources of the framework (not the distribution),
we provide a
flexive.ipr.sample
project sample file which you can copy to
flexive.ipr
and start coding and exploring right away.
The blank [fleXive] application includes an example EJB and JSF bean that shows the intended use of the package structure, cfe stands for com.flexive.example:
src/java/shared/cfe.shared.interfaces.EJBExample
Defines the (remote) interface of an EJB.
src/java/ejb/cfe.ejb.EJBExampleBean
Contains the EJB implementation of the
EJBExample
interface.
src/java/war/cfe.war.ManagedExampleBean
A sample implementation of a JSF wrapper bean that invokes
an EJB business method. Note the use of flexive's
EJBLookup
class to retrieve the EJB wrapper bean, since the
@EJB
annotation isn't supported by all application servers at the time
of this writing.
You can remove the example beans if you like, or rename them and use them as a start for your own beans (if any).
A [fleXive] application is a collection of JAR files that follow a few naming conventions
that will be packaged into an EAR archive. Note
that each file is optional, so if you for example don't provide EJB3 beans, you don't need
to provide a
-ejb.jar
file.
[basename]-ejb.jar
Contains the EJB3 implementations for your component.
[basename]-shared.jar
Contains shared classes of your application that are not tied to either the JSF or the EJB layer. This contains the EJB interface definitions, data transfer objects or general utility classes.
[basename].war
Contains the actual web application frontend, consisting of HTML pages, JSF components and Facelets templates, as well as JSF managed beans and deployment descriptors.
*.jar
Of course you can also package any additional JAR files containing
libraries or even JSF plugins. They will be stored in the
lib/
folder of the EAR file and will be available
to every [fleXive] application in the EAR file.
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</id> <name>JBoss Maven Repository</name> <url>http://repository.jboss.org/maven2</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.
The snapshot versions in our Maven repository (
3.0-SNAPSHOT
and
3.1-SNAPSHOT)
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:
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]).
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.
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.)
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:
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.
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>
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>
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>
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>
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>
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>
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
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.
| 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.
Example 6.1. Creating a new user
import com.flexive.shared.EJBLookup; import com.flexive.shared.interfaces.AccountEngine; import com.flexive.shared.security.AccountEdit AccountEngine ae = EJBLookup.getAccountEngine(); AccountEdit accountEdit = new AccountEdit() accountEdit.setEmail("admin@flexive.org") accountEdit.setName("admin") long accountId = ae.create(accountEdit, "password")
Returns the ID of the created account.
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] provides two methods for updating an account. One is for updating only some personal data of the specified user (name, login name, password, e-mail address, and language). The other one lets you update all attributes of the user. For both methods applies: setting one parameter to null in the method call means keeping its original value (i.e. the value of the corresponding account attribute is not changed).
The following code updates the previously created administrator account (ID 1) by setting its name to “System administrator” and its e-mail address to “sysadmin@flexive.org”.
Example 6.2. Updating a user
import com.flexive.shared.EJBLookup; import com.flexive.shared.interfaces.AccountEngine; AccountEngine ae = EJBLookup.getAccountEngine(); ae.update(1, null, “System administrator”, null, "sysadmin@flexive.org", null);
Note that by setting the parameters password, login name and language to null the original values (“pw123”, “admin”, 1) are preserved.
There are two prerequisites for the caller to remove an account:
the caller must be in role ACCOUNT MANAGEMENT
the account/user has to belong to the callers mandator
GlobalSupervisor may remove users belonging to any mandator. The accounts USER_GUEST and USER_GLOBAL_SUPERVISOR may not be removed in any case.
For removing an account one only needs to know the account ID. Thus removing the previously created and updated administrator account works as follows:
[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”.
Authentication is currently performed against the database by calling
FxContext.login(..)
(or as an alternative directly using
AccountEngine.login(..)).
JAAS based login can be implemented very easily by uncommenting some code in
LoginLogoutHandler. It is planned to make this configurable in future versions
of [fleXive].
At the core and web layer (provided they run in the same virtual machine) a context is available which
is stored in a
ThreadLocal
called
FxContext
which provides amongst others the following information:
getDivisionId(): number of the current division
getLanguage(): the current users preferred language
getTicket(): the current users
UserTicket
instance
The
UserTicket
is the central class providing information about the current user ranging from the user name, mandator, user
group memberships, assigned roles to all access control lists the user belongs to indirectly. Hence it
serves as the central authorization data provider for any security check performed.
[fleXive] was designed to be used by application service providers (ASP) with only one deployed enterprise application instance using multiple databases. These database instances are called Divisions and contain their own accounts, structures and content - totally separated from other users of [fleXive].
Divisions are mapped to server names using regular expressions. A web based user interface is provided by the Global Configuration Plugin.
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:
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”.
| 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).
A system internal and somewhat special type is the "Root" type: It serves as a virtual repository to which groups or properties can be assigned, which in turn can be reused in other types or assignments without the need of inheritance.
The following is a list of parameters that can be passed as arguments to
FxTypeEdit
when editing or creating a new type:
| 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:
|
| 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.
|
| 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:
|
| 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:
|
| 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.
|
| 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:
Example 6.4. Creating a new FxType
FxTypeEdit.createNew("Customer") //.setLabel( //
new FxString(FxLanguage.ENGLISH, "A generic customer") .setTranslation(FxLanguage.GERMAN, "Ein generischer Kunde")) .setACL(CacheAdmin.getEnvironment().getACL(ACLCategory.STRUCTURE.getDefaultId())) //
.save(); //
FxType typeByName = CacheAdmin.getEnvironment().getType("Customer"); //
![]()
|
Create a new editable FxType instance with the type name "Customer". The type name is unique for the entire division and can be used to lookup the type afterwards. | |
|
The multilingual type label is initialized with an English and German type label. | |
|
Assign an ACL that is used for all instances of that type. Note that you can skip this step if you want to use the default structure ACL (like in this example). | |
|
The type is created in the database. You can add properties to the type as shown in Example 6.13, “Creating a "Person" type in Java”. | |
|
This line just serves as an example how to retrieve the a reference to the new type. Saving the instance automatically forced a reload of the cached environment, that why we can retrieve it from the cache here. |
Another way to create a new type would be to use the
GroovyTypeBuilder:
Example 6.5. Creating a new FxType using the GroovyTypeBuilder
import com.flexive.shared.scripting.groovy.* import com.flexive.shared.value.* import com.flexive.shared.security.* import com.flexive.shared.* def typeDesc = new FxString("A generic customer") typeDesc.setTranslation(FxLanguage.GERMAN, "Ein generischer Kunde") new GroovyTypeBuilder().customer(description: typeDesc, acl: CacheAdmin.environment.getACL(ACLCategory.STRUCTURE.defaultId)) { } def type = CacheAdmin.environment.getType("Customer") return type.name
If executed in the Groovy-Console the code above would return CUSTOMER.
Every data type can potentially support values for different languages - depending on the properties' and property assignments' multilingual support settings.
| 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.
Example 6.6. Handling binary content
File testFile = new File("src/framework/testresources/image/Exif.JPG"); FxType type = CacheAdmin.getEnvironment().getType(IMAGE_TYPE); FileInputStream fis = new FileInputStream(testFile); BinaryDescriptor binary = new BinaryDescriptor(testFile.getName(), testFile.length(), fis); //This example shows how easy it is to upload a binary programatically to theFxContent img = co.initialize(type.getId()); img.setValue("/File", new FxBinary(false, binary)); //
img.setValue("/Filename", new FxString(false, "Exif.JPG")); FxPK pk = co.save(img); FxContent loaded = co.load(pk); FxBinary bin = (FxBinary)loaded.getValue("/File"); File comp = File.createTempFile("Exif","JPG"); FileOutputStream fos = new FileOutputStream(comp); bin.getBestTranslation().download(fos); //
fos.close();
ContentEngine
and download it from the saved instance.
|
A
| |
|
Simply set a
| |
|
Downloading binary content is as simple as providing an
|
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.
The following is a list of parameters that can be passed as arguments to
FxSelectListEdit
when editing or creating a new select list:
| Parameter | Method | Description |
|---|---|---|
| Parent list |
-
|
Set the parent list of this select list (used for nested select lists). Can only be set when the select list is created. |
| Name |
setName(String name)
|
Set the name of the select list (has to be unique). |
| Label |
setLabel(FxString label)
|
Set a label text to be displayed in user interfaces. |
| Description |
setDescription(FxString description)
|
Set a description text to be displayed in user interfaces. |
| Allow dynamic item creation |
-
|
May items be created dynamically (in UIs other than the administration GUI). Can only be set when the select list is created. This flag is merely used as information for UI's where users may create select list items themselves. Independent of this flag the users can only create items if they have the permission (see Table 6.7, “Relevant permissions for working with select list items” for details). |
| Create Item ACL |
-
|
The ACL which controls the permissions for adding and removing select items to the select list. Can only be set when the select list is created. |
| New Item ACL |
-
|
The ACL which is assigned to newly created items by default. Can only be set when the select list is created. |
| Default Item |
setDefaultItem(FxSelectListItem defaultItem)
|
A select list item, which is selected by default. |
The following is a list of parameters that can be passed as arguments to
FxSelectListItemEdit
when editing or creating a new select list item:
| Parameter | Method | Description |
|---|---|---|
| List |
-
|
The select list to which this select item belongs. |
| Name |
setName(String name)
|
Set the name of the item (has to be unique within the assigned list). |
| Parent Item |
setParentItem(FxSelectListItem item)
|
Set the parent item of this select list item (used for nested select lists). |
| ACL |
setAcl(ACL acl)
|
The ACL of the select item (see Table 6.7, “Relevant permissions for working with select list items” for details). |
| Label |
setLabel(FxString label)
|
Set a label text to be displayed in user interfaces. |
| Data |
setData(String data)
|
Set additional data stored in the select item. |
| Color |
setColor(String color)
|
Set the item's display color. |
| Icon Id |
setIconId(long iconId)
|
Set the id of this select list item's icon. |
| Icon version |
setIconVer(int iconVer)
|
Set the version of the icon to be displayed. |
| Icon quality |
setIconQuality(int iconQuality)
|
Set the quality of the icon to be displayed. |
The item's Life Cycle Information getLifeCycleInfo() is generated and managed by
the database and can not be edited.
As mentioned earlier, two kinds of select lists can be created. The first kind is
not suitable for being persisted and is used for example for quick display
in UI input components. FxSelectList provides
FxSelectList(String name),
createList(String name, List<? extends SelectableObjectWithLabel> items) and
createListWithName(String name, List<? extends SelectableObjectWithName> items)
for these purposes. FxSelectListItem provides
FxSelectListItem(long id, FxSelectList list, long parentItemId, FxString label)
for select list items that are not persisted.
The second kind of select lists, which can be persisted, are created via FxSelectListEdit and
its constructor or createNew(..) helper methods.
Similar to persistable select lists, persistable select list items are created via
the FxSelectListItemEdit class by invoking its constructor or its
createNew(..) method.
The persistance is handled via methods provided
by theSelectListEngine.
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”.
| 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. |
Multiplicity - or often referred to as
cardinality
- defines the minimum and maximum occurance of an assignment. An assignment in the context of [fleXive] is a
group- or a property assignment. Multiplictiy is implemented in the class
FxMultiplicity
which contains some predefined and often used constants:
FxMultiplicity.MULT_0_1: zero or one occurances (optional)
FxMultiplicity.MULT_1_1: exactly one occurance (required)
FxMultiplicity.MULT_0_N: zero or more occurances (optional)
FxMultiplicity.MULT_1_N: one or more occurances (required)
The value
N
means any number of occurances.
A multiplicity of 1 to 5 elements would be coded like this:
new FxMultiplicity(1,5)
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
AssignmentEngine assignmentEngine = EJBLookup.getAssignmentEngine();
ACL customerACL = CacheAdmin.getEnvironment().getACL(ACLCategory.STRUCTURE.getDefaultId());
FxPropertyEdit name = FxPropertyEdit.createNew("Name",
new FxString("Name of the person"),
new FxString("Enter the persons name"),
FxMultiplicity.MULT_1_1,
customerACL,
FxDataType.String1024);
assignmentEngine.createProperty(typeId, name.setAutoUniquePropertyName(true), "/");
|
Obtain a reference to the assignment engine bean which is responsible for manipulating assignments | |
|
Get the default ACL used for structures | |
|
Create a new property with the alias "Name" | |
|
Label | |
|
Hint for user interfaces | |
|
Configure this property to be required (See the section called “Multiplicity” for more information) | |
|
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 | |
|
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. | |
|
Since properties can not exist if they are not assigned to a type, we assign them to the type
with the id
|
To reuse an existing property-assignment is even simpler:
Example 6.8. Reusing property assignments
assignmentEngine.save(
FxPropertyAssignmentEdit.reuse(
"ROOT/CAPTION",
"Customer",
"/",
"AnotherCaption"),
false);
|
Obtaining a reference to the assignment engine bean is omitted in this example. We use the
| |
|
The convenience factory method
| |
|
The XPath of the assignment that is to be reused in, preceeded by the type. Here we reuse the "Caption" property that is assigned to the virtual Root-Type which acts as a repository (see the section called “Root Type” for more information about the virtual root type). | |
|
The type we want to assign it to. Please note that the type name is not case sensitive. | |
|
The new parent XPath relative to the type. Since want to assign it to the root group we use "/". Would we want to assign the property to a group called "Info" which is a subgroup of a group named "About", we'd use the XPath "/About/Info". | |
|
The
| |
|
This boolean parameter is used if the assignment is a group assignment. If set to |
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"),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")
builder { firstname(dataType: FxDataType.String1024,
multilang: false, label: new FxString(FxLanguage.ENGLISH, "A person's first name"),
multiplicity: FxMultiplicity.MULT_0_N) } new GroovyTypeBuilder().anotherperson(label: new FxString("Anotherperson"),
acl: CacheAdmin.environment.getACL(ACLCategory.STRUCTURE.defaultId), multilang: true) { anotherfirstname(assignment: "PERSON/FIRSTNAME",
label: new FxString(FxLanguage.ENGLISH, "Another's first name"))
} def name = "" def type = CacheAdmin.getEnvironment().getType("person") FxPropertyAssignment propAssign = type.getPropertyAssignment("PERSON/FIRSTNAME") name = propAssign.getLabel() EJBLookup.getAssignmentEngine().removeAssignment(propAss.getId(), false, false);
FxProperty prop = CacheAdmin.getEnvironment().getProperty("firstname");
return "Removed assigned (label): " + name + "; Retained label for property: " + String.valueOf(prop.getLabel())
![]()
|
The type "Person" is created. | |
|
The
| |
|
Here we create / assign the property "firstname" and .. | |
|
.. set its label to "A person's first name". Note that the
| |
|
The type "Anotherperson" is created .. | |
|
.. and the firstname property is assigned to this type having the alias "anotherfirstname". | |
|
The label is set to "Another's first name". | |
|
After temporarily storing the property's label (for later reference) we remove the assignment of the property "firstname" from the type "Person". | |
|
Since only the assignment was removed, we can still load the "original" property. | |
|
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:
|
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:
| 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.
|
| 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). |
| 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) |
[fleXive] supports the following modes to enforce that a value is unique:
None: No uniqueness is enforced
Global: Globally unique across all usages of a property
Type: Unique within all instances of a type
DerivedTypes: Unique within all instances of a type, its parents and
types derived from it
Instance: Unique within a content instance
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
AssignmentEngine assignmentEngine = EJBLookup.getAssignmentEngine();
assignmentEngine.createGroup(
typeId,
FxGroupEdit.createNew(
"Address",
new FxString("The customers address"),
new FxString("Enter the customers address here"),
true,
FxMultiplicity.MULT_1_1).
setAssignmentGroupMode(GroupMode.AnyOf),
"/");
FxPropertyEdit street = FxPropertyEdit.createNew(...);
FxPropertyEdit zip = FxPropertyEdit.createNew(...);
assignmentEngine.createProperty(typeId, street, "/Address");
assignmentEngine.createProperty(typeId, zip, "/Address");
|
Obtain a reference to the assignment engine which is needed to create groups, properties and (of course) assignments | |
|
We're about to create a new group | |
|
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. | |
|
The second parameter to
| |
|
"Address" is the name we chose for the new group | |
|
The label (for user interfaces) | |
|
The hint (for user interfaces again) | |
|
This parameter allows overriding the multiplicity assigned in the next line by assignments to the group | |
|
We make the group required, setting the multiplictiy to 1..1 (See the section called “Multiplicity” for more information) | |
|
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
| |
|
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. | |
|
We create a property called "Street" which we want to assign to the address group later. | |
|
Same for the property "ZIP" | |
|
The "Street" property is created like in the section called “Properties and property assignments”, but we assign it to our new created group "/Address" | |
|
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.*new GroovyTypeBuilder().person(label: new FxString("Person"),
acl: CacheAdmin.environment.getACL(ACLCategory.STRUCTURE.defaultId),
multilang: true) {
firstname(dataType: FxDataType.String1024, multilang: false, label: new FxString(FxLanguage.ENGLISH, "First name"), multiplicity: FxMultiplicity.MULT_0_N)
lastname(assignment: "PERSON/FIRSTNAME", label: new FxString(FxLanguage.ENGLISH, "Last name"), hint: new FxString(FxLanguage.ENGLISH, "Last name required"), multiplicity: FxMultiplicity.MULT_1_N)
ADDRESS(label: new FxString(FxLanguage.ENGLISH, "Address"), multiplicity: FxMultiplicity.MULT_0_N) {
street(dataType: FxDataType.String1024, label: new FxString(FxLanguage.ENGLISH, "Street (Nr)"), multiplicity: FxMultiplicity.MULT_0_N) } }
def builder = new GroovyContentBuilder("PERSON")
builder {
firstname("John")
lastname("Doe")
address {
street(new FxString(false, "Ameaningfulstreetname 444")) } }
EJBLookup.getContentEngine().save(builder.getContent())
|
Create the type "Person" using the
| |
|
.. use the default ACLs .. | |
|
.. and set the type to support multiple languages. | |
|
Create the first property "firstname". | |
|
Another property for the last name (implicitly required because auf the default multiplicity of 1..N), which is a derived assignment from "FIRSTNAME". | |
|
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. | |
|
The group's only property: "street". | |
|
Retrieve a
| |
|
Call the builder passing the parameters (XPathname([contentValue]): | |
|
Set the firstname to "John". | |
|
Set the lastname to "Doe". | |
|
"Open up" the group's XPath value using "address". | |
|
Pass the value "Ameaningfulstreetname 444" to the XPath ADDRESS/STREET | |
|
Save the content by retrieving the ContentEngine EJB and calling its
|
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:
| 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”. |
| 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). |
| 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) |
[fleXive] supports the two modes for groups
OneOf
andAnyOf:
OneOf: Only one of the groups children may be present, honoring their
regular indices. This mode only makes sense if all subgroups/properties are optional!
AnyOf: Any of the groups children may be present, honoring their regular
indices. This is the default setting.
Both properties and groups allow the definition of options. These options can hold any arbitrary
String
or
Boolean
value and, if defined, the assignments to the property or group may override the value. The name of an option
must be unique.
See the API documentation of class FxStructureOption for more details.
The following options are currently actively supported:
| Constant Value | Description |
|---|---|
| MULTILANG |
Value supports multiple languages (
true
/
false
)
|
| SHOW.OVERVIEW |
Values are shown in UI overview components (
true
/
false
)
|
| HTML.EDITOR |
Value can be edited using an HTML editor in user interfaces (
true
/
false
)
|
| SEARCHABLE |
Values can be queried (this is just a hint for user interfaces!) (
true
/
false
)
|
| MAXLENGTH |
Values that support a maximum input length (currently only
String1024
and
Text
) will be rendered with input length restrictions when editing.
|
| MULTILINE | Render a textarea instead of a single input line. If the value is greater than 1 it determines the number of rows. |
| REFERENCE.SELECTONE | Render a select list with all valid choices for reference properties (properties referencing other contents). |
| SELECTMANY.CHECKBOXES | Render checkboxes instead of a multi-select list for FxSelectMany properties. |
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):
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":
Example 6.12. Creating a simple type with one attribute
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.*The type and its attribute were created using the default settings for both types and properties (i.e. for the "NAME" this means thenew GroovyTypeBuilder().person {
name() }
FxDataType
"String")
More on default settings and all available attributes can seen in
the section called “GroovyTypeBuilder Attributes”
and in
the section called “Property and Group Creation - Default Values”.
If you would like to get right into the thick of it, please check out the example section
the section called “GroovyTypeBuilder Usage Examples”.
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:
Groupname {
}
or have parentheses w/o curly braces, as in
Groupname ()
propertyname (label: new FxString("Property label"), multilang: false)
Creation and change of structural elements
GroovyTypeBuilder's constructor,
giving the type's name as the build parameter (e.g.: type name = "BLOG"
new GroovyTypeBuilder().blog {
}
or
new GroovyTypeBuilder().blog()
or by assigning a builder variable:
def builder = new GroovyTypeBuilder()
builder.blog {
}
new GroovyTypeBuilder().blog(label: new FxString(true, "A Blog Type"),
trackHistory: true,
historyAge: 10L) {
}
or
def builder = new GroovyTypeBuilder()
builder.blog(label: new FxString(true, "A Blog Type"),
trackHistory: true,
historyAge: 10L) {
// ... properties & groups here ...
}
new GroovyTypeBuilder().blog {
title()
Entry {
text()
}
}
Loading a type (more on this in the section called “GroovyTypeBuilder Usage Examples”.
Types can either be loaded by passing the type's name to the GTB as in the following example, e.g. loading the type "BLOG"
def builder = new GroovyTypeBuilder("BLOG")
builder {
// do something with "BLOG"
}
Alternatively (and quite useful if you do a lot of "copy-and-paste" with your script code), the type can also be loaded using the already presented constructor: When called, the type will be loaded if it already exists, otherwise a new type bearing the given name will be created.
// "BLOG" already exists
def builder = new GroovyTypeBuilder().blog {
// do something with "BLOG"
}
def builder = new GroovyTypeBuilder("BLOG")
builder {
'abstract'(label: new FxString(true, "Abstract Prop"),
dataType: FxDataType.Text)
}
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:
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"))
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.
| 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. |
| 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>
|
|
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 |
| 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>
|
|
Set the group's or property's options via a List of FxStructureOptions.
|
| 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
|