For your first [fleXive] application, 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 in [fleXive] source packages or in the subversion repository under
src/examples.
Our first [fleXive] application implements a very simple blogging system with an input mask and a view for the submitted entries. Looking at the source directory of the application, we find the following directory structure:
/helloworld/ | +--build.xml /helloworld/java/ /helloworld/java/com.flexive.examples.helloworld.war | +--HelloWorldBean.java /helloworld/resources/ /helloworld/resources/scripts/ /helloworld/resources/scripts/library/ /helloworld/resources/scripts/startup/ /helloworld/resources/scripts/runonce/ | +--hello001.groovy /helloworld/web/ | +--create.xhtml +--index.xhtml /helloworld/web/WEB-INF/ | +--faces-config.xml +--web.xml
java
contains the Java sources of the application.
resources/scripts
contains [fleXive] scripts that will be used to setup the data structures needed by the application.
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 use the
run-once script
hello001.groovy
located in the
resources/scripts/runonce/
directory.
The easiest way of setting up [fleXive] structures is by using the Groovy builder for
[fleXive] types,
GroovyTypeBuilder.
import com.flexive.shared.scripting.groovy.*
import com.flexive.shared.value.*
import com.flexive.shared.structure.*
new GroovyTypeBuilder().blogEntry(description: new FxString(true, "Blog Entry"), usePermissions: false) { //
caption(assignment: "ROOT/CAPTION")
entryTitle(multiplicity: FxMultiplicity.MULT_1_1, description: new FxString(true, "Title")) //
entryText(multiplicity: FxMultiplicity.MULT_1_1, description: 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 description 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.
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 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/>
</head>
<body>
<h:form>
<!-- Display all JSF messages -->
<h:messages/>
<h:outputLink value="index.xhtml">Back to blog</h:outputLink>
<!-- 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:commandLink action="#{fxContentViewBean.save}">
<f:setPropertyActionListener target="#{fxContentViewBean.content}" value="#{entry_content}"/>
Publish
</h:commandLink>
</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>
</body>
</html>
In the
web/WEB-INF
directory there are two files:
The web deployment descriptor in
web.xml.
Here we initialize the JSF runtime and add the
FxFilter
for pages handled by [fleXive].
FxFilter
is required for more advanced [fleXive] functionality, but is not required
for this application.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- Use Documents Saved as *.xhtml -->
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param>
<!-- Configure FxFilter -->
<filter>
<filter-name>FxFilter</filter-name>
<filter-class>com.flexive.war.filter.FxFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>FxFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Configure Faces servlet -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<!--
/faces/* (or another suffix pattern) should always be the last url-pattern for the FacesServlet,
otherwise the flexive Weblets includes won't work
-->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.xhtml</welcome-file>
</welcome-file-list>
<listener>
<listener-class>net.java.dev.weblets.WebletsContextListener</listener-class>
</listener>
<listener>
<listener-class>com.flexive.war.listener.SessionTimeoutListener</listener-class>
</listener>
</web-app>
The JSF configuration file in
faces-config.xml.
Here we register the
HelloWorldBean
and provide navigation rules between the overview and the create page.
<?xml version='1.0' encoding='UTF-8'?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
<!-- Facelets View Handler Definition -->
<application>
<view-handler>
com.sun.facelets.FaceletViewHandler
</view-handler>
</application>
<!-- Definition of the managed bean which retrieves the data from database -->
<managed-bean>
<managed-bean-name>helloWorldBean</managed-bean-name>
<managed-bean-class>com.flexive.examples.helloworld.war.HelloWorldBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<!-- A navigation rule for our input mask, if everything goes right,
"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>
</faces-config>
To compile and deploy the framework and the example application run the following ant tasks in the given order (you need a full [fleXive] source installation for this):
ant dist
in your
${flexive.home}
directory where the
build.xml
file resides if you don't
already have a
flexive-dist
directory
ant
in the
${flexive.home}/src/examples/helloworld/
directory
ant deploy
in your
${flexive.home}
directory
Start your application server and point your browser to http://localhost:8080/helloworld/index.xhtml Congratulations! You should see a link to the input mask.
After the successful deployment of the application you can find the created datastructure in the backend administration's structure editor. Log on to the backend administration and expand the tab named "Structure". 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.
