Author: julien(a)jboss.com
Date: 2007-06-15 08:02:23 -0400 (Fri, 15 Jun 2007)
New Revision: 7436
Added:
docs/trunk/referenceGuide/en/modules/contentintegration.xml
Log:
renaming contentIntegration to contentintegration
Added: docs/trunk/referenceGuide/en/modules/contentintegration.xml
===================================================================
--- docs/trunk/referenceGuide/en/modules/contentintegration.xml
(rev 0)
+++ docs/trunk/referenceGuide/en/modules/contentintegration.xml 2007-06-15 12:02:23 UTC
(rev 7436)
@@ -0,0 +1,451 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter id="contentIntegration">
+ <chapterinfo>
+ <author>
+ <firstname>Julien</firstname>
+ <surname>Viet</surname>
+ <email>julien @ jboss dot com</email>
+ </author>
+ </chapterinfo>
+ <title>Content Integration</title>
+ <para>Since JBoss Portal 2.6 it is possible to provide an easy integration of
content within the portal. Up to the 2.4 version
+ content integration had to be done by configuring a portlet to show some content from
an URI and then place that
+ portlet on a page. The new content integration capabilities allows to directly
configure a page window with the content URI
+ removing the need to configure a portlet for that purpose.</para>
+ <note>We do not advocate to avoid the usage portlet preferences, we rather
advocate that content configuration managed at the portal level
+ simplifies the configuration: it helps to make content a first class citizen of the
portal instead of having an intermediary
+ portlet that holds the content for the portal. The portlet preferences can still be
used to configure how content is displayed
+ to the user.</note>
+ <mediaobject>
+ <imageobject>
+ <imagedata align="center"
fileref="images/content/before.png" format="png"/>
+ </imageobject>
+ <caption>
+ <para>The portal uses portlets to configure content</para>
+ </caption>
+ </mediaobject>
+ <mediaobject>
+ <imageobject>
+ <imagedata align="center"
fileref="images/content/after.png" format="png"/>
+ </imageobject>
+ <caption>
+ <para>The portal references directly the content and use portlet to
interact with content</para>
+ </caption>
+ </mediaobject>
+ <sect1>
+ <title>Window content</title>
+ <para>The content of a window is defined by
+ <itemizedlist>
+ <listitem>The content URI which is the resource that the window is
pointing to. It is an arbitrary string that
+ the portal cannot interpret and is left up to the content provider to
interpret.</listitem>
+ <listitem>The window content type which defines how the portal
interpret the window content
+ <itemizedlist>
+ <listitem>The default content type is for portlets and has the
value <emphasis>portlet</emphasis>. For that
+ content type, the content URI is the portlet instance
id.</listitem>
+ <listitem>The CMS content type allows to integrate content from
the CMS at the page and it has the value
+ <emphasis>cms</emphasis>. For that content type, the
content URI is the CMS file path.</listitem>
+ </itemizedlist>
+ </listitem>
+ <listitem>The content parameters which is a set of additional key/value
string pairs holding state that is interpreted
+ by the content provider.</listitem>
+ </itemizedlist>
+ </para>
+ <para>At runtime when the portal needs to render a window it delegates the
production of markup to a content provider.
+ The portal comes with a preconfigured set of providers which handles the portlet
and the cms content types. The most
+ natural way to plug a content provider in the portal is to use a JSR 168 Portlet.
Based on a few carefully chosen conventions
+ it is possible to provide an efficient content integration with the benefit of
using standards and without requiring
+ the usage of a proprietary API.</para>
+ </sect1>
+ <sect1>
+ <title>Content customization</title>
+ <para>Content providers must be able to allow the user or administrator to
chose content from the external resource
+ it integrates in the portal in order to properly configure a portal window. A few
interactions between the portal, the content
+ provider and the portal user are necessary to achieve that goal. Here again it is
possible to provide content
+ customization using a JSR 168 Portlet. For that purpose two special portlet modes
called
+ <emphasis>edit_content</emphasis> and
<emphasis>select_content</emphasis> has been introduced. It signals to the
portlet
+ that it is selecting or editing the content portion of the state of a portlet.
<emphasis>select_content</emphasis> is
+ used to select a new content to put in a window while
<emphasis>edit_content</emphasis> is used to modify the previously
+ defined content, often the two modes will display the same thing. The traditional
edit mode is not used because the edit mode
+ is more targetted to configure how the portlet show content to the end user rather
than what content it shows.</para>
+ <imageobject>
+ <imagedata align="center"
fileref="images/content/cms.png" format="png"/>
+ </imageobject>
+ <caption>Example of content customization - CMS Portlet</caption>
+ </sect1>
+ <sect1>
+ <title>Content Driven Portlet</title>
+ <para>Portlet components are used to integrate content into the portal. It
relies on a few conventions which allow
+ the portal and the portlet to communicate.
+ </para>
+ <sect2>
+ <title>Displaying content</title>
+ <para>At runtime the portal will call the portlet with the view mode when
it displays content. It will send to the
+ portlet the information about the content to display using the render
parameters. Therefore the portlet has
+ just to read the render parameters and use them to properly display the content
in the portlet. The render parameters
+ values are the key/value pairs that forms the content properties and the
resource URI is found under the <emphasis>uri</emphasis>
+ parameter name.</para>
+ </sect2>
+ <sect2>
+ <title>Configuring content</title>
+ <para>As explained before, the portal will call the portlet using the
<emphasis>edit_content</emphasis> mode.
+ In that mode the portlet and the portal will communicate using either action or
render parameters. We have two use cases
+ which are:
+ <itemizedlist>
+ <listitem>The portal needs to configure a new content item for a new
window. In that use case the portal will not send special
+ render parameters to the portlet and the initial set of render parameters
will be empty. The portlet can
+ then use render parameters in order to provide navigation in the content
repository. For example the portlet
+ can navigate the CMS tree and store the current CMS path in the render
parameters. Whenever the portlet has decided
+ to tell the portal that content has been selected by the user it needs to
use an action URL with a special set of
+ parameters:
+ <itemizedlist>
+
<listitem><emphasis>content.action.select</emphasis> equals to any
value</listitem>
+ <listitem><emphasis>content.uri</emphasis> equals
to the content URI</listitem>
+ <listitem><emphasis>content.param.</emphasis> used
as prefix to configure content parameters</listitem>
+ </itemizedlist>
+ </listitem>
+ <listitem>The second use case happens when the portal needs to edit
existing content. In such situation
+ everything works as explained before except that the initial set of render
parameters of the portlet
+ will be prepopulated with the content uri URI and
parameters.</listitem>
+ </itemizedlist>
+ </para>
+ </sect2>
+ <sect2>
+ <title>Step by step example of a content driven portlet</title>
+ <para></para>
+ <sect3>
+ <title>The Portlet skeleton</title>
+ <para>Here is the base skeleton of the content portlet. The
FSContentDrivenPortlet shows the files which are
+ in the war file in which the portlet is deployed. The arbitrary name
<emphasis>filesystem</emphasis>
+ will be the content type interpreted by the portlet.
+ </para>
+ <programlisting><![CDATA[
+public class FSContentDrivenPortlet extends GenericPortlet
+{
+
+ /** The edit_content mode. */
+ public static final PortletMode EDIT_CONTENT_MODE = new
PortletMode("edit_content");
+
+ /** The select_content mode. */
+ public static final PortletMode SELECT_CONTENT_MODE = new
PortletMode("select_content");
+
+ ...
+
+}
+]]></programlisting>
+ </sect3>
+ <sect3>
+ <title>Overriding the dispatch method</title>
+ <para>First the <emphasis>doDispatch(RenderRequest req,
RenderResponse resp)</emphasis> is overriden in order
+ to branch the requeset flow to a method that will take care of displaying the
editor.</para>
+ <programlisting><![CDATA[
+protected void doDispatch(RenderRequest req, RenderResponse resp)
+ throws PortletException, PortletSecurityException, IOException
+{
+ if (EDIT_CONTENT_MODE.equals(req.getPortletMode()) ||
SELECT_CONTENT_MODE.equals(req.getPortletMode()))
+ {
+ doEditContent(req, resp);
+ }
+ else
+ {
+ super.doDispatch(req, resp);
+ }
+}
+]]></programlisting>
+ </sect3>
+ <sect3>
+ <title>Utilities methods</title>
+ <para>The portlet also needs a few utilities methods which take care of
converting content URI to a file
+ back and forth. There is also an implementation of a file filter that keep
only text files and avoid the
+ WEB-INF directory of the war file for security reasons.</para>
+ <programlisting><![CDATA[
+protected File getFile(String contentURI) throws IOException
+{
+ String realPath = getPortletContext().getRealPath(contentURI);
+ if (realPath == null)
+ {
+ throw new IOException("Cannot access war file content");
+ }
+ File file = new File(realPath);
+ if (!file.exists())
+ {
+ throw new IOException("File " + contentURI + " does not
exist");
+ }
+ return file;
+}
+]]></programlisting>
+ <programlisting><![CDATA[
+ protected String getContentURI(File file) throws IOException
+ {
+ String rootPath = getPortletContext().getRealPath("/");
+ if (rootPath == null)
+ {
+ throw new IOException("Cannot access war file content");
+ }
+
+ // Make it canonical
+ rootPath = new File(rootPath).getCanonicalPath();
+
+ // Get the portion of the path that is significant for us
+ String filePath = file.getCanonicalPath();
+ return filePath.length() >=
+ rootPath.length() ? filePath.substring(rootPath.length()) : null;
+ }
+]]></programlisting>
+ <programlisting><![CDATA[
+ private final FileFilter filter = new FileFilter()
+ {
+ public boolean accept(File file)
+ {
+ String name = file.getName();
+ if (file.isDirectory())
+ {
+ return !"WEB-INF".equals(name);
+ }
+ else if (file.isFile())
+ {
+ return name.endsWith(".txt");
+ }
+ else
+ {
+ return false;
+ }
+ }
+ };
+]]></programlisting>
+ </sect3>
+ <sect3>
+ <title>The editor</title>
+ <para>The editor is probably the longest part of the portlet. It tries
to stay simple though and goes directly
+ to the point.</para>
+ <imageobject>
+ <imagedata align="center"
fileref="images/content/fs1.png" format="png"/>
+ </imageobject>
+ <caption>Content editor of FSContentDrivenPortlet in
action</caption>
+ <programlisting><![CDATA[
+protected void doEditContent(RenderRequest req, RenderResponse resp)
+ throws PortletException, PortletSecurityException, IOException
+{
+ // Get the uri value optionally provided by the portal
+ String uri = req.getParameter("content.uri");
+
+ // Get the working directory directory
+ File workingDir;
+ if (uri != null)
+ {
+ workingDir = getFile(uri).getParentFile();
+ }
+ else
+ {
+ // Otherwise try to get the current directory we are browsing,
+ // if no current dir exist we use the root
+ String currentDir = req.getParameter("current_dir");
+ if (currentDir == null)
+ {
+ currentDir = "/";
+ }
+ workingDir = getFile(currentDir);
+ }
+
+ // Get the parent path
+ String parentPath = getContentURI(workingDir.getParentFile());
+
+ // Get the children of the selected file, we use a filter
+ // to retain only text files and avoid WEB-INF dir
+ File[] children = workingDir.listFiles(filter);
+
+ // Configure the response
+ resp.setContentType("text/html");
+ PrintWriter writer = resp.getWriter();
+
+ //
+ writer.print("Directories:<br/>");
+ writer.print("<ul>");
+ PortletURL choseDirURL = resp.createRenderURL();
+ if (parentPath != null)
+ {
+ choseDirURL.setParameter("current_dir", parentPath);
+ writer.print("<li><a href=\"" + choseDirURL +
"\">..</a></li>");
+ }
+ for (int i = 0;i < children.length;i++)
+ {
+ File child = children[i];
+ if (child.isDirectory())
+ {
+ choseDirURL.setParameter("current_dir", getContentURI(child));
+ writer.print("<li><a href=\"" + choseDirURL +
"\">" + child.getName() +
+ "</a></li>");
+ }
+ }
+ writer.print("</ul><br/>");
+
+ //
+ writer.print("Files:<br/>");
+ writer.print("<ul>");
+ PortletURL selectFileURL = resp.createActionURL();
+ selectFileURL.setParameter("content.action.select", "select");
+ for (int i = 0;i < children.length;i++)
+ {
+ File child = children[i];
+ if (child.isFile())
+ {
+ selectFileURL.setParameter("content.uri", getContentURI(child));
+ writer.print("<li><a href=\"" + selectFileURL +
"\">" + child.getName() +
+ "</a></li>");
+ }
+ }
+ writer.print("</ul><br/>");
+
+ //
+ writer.close();
+}
+]]></programlisting>
+ </sect3>
+ <sect3>
+ <title>Viewing content at runtime</title>
+ <para>Last but not least the portlet needs to implement the
<emphasis>doView(RenderRequest req, RenderResponse resp)</emphasis>
+ method in order to display the file that the portal window wants to
show.</para>
+ <programlisting><![CDATA[
+protected void doView(RenderRequest req, RenderResponse resp)
+ throws PortletException, PortletSecurityException, IOException
+{
+ // Get the URI provided by the portal
+ String uri = req.getParameter("uri");
+
+ // Configure the response
+ resp.setContentType("text/html");
+ PrintWriter writer = resp.getWriter();
+
+ //
+ if (uri == null)
+ {
+ writer.print("No selected file");
+ }
+ else
+ {
+ File file = getFile(uri);
+ FileInputStream in = null;
+ try
+ {
+ in = new FileInputStream(file);
+ FileChannel channel = in.getChannel();
+ byte[] bytes = new byte[(int)channel.size()];
+ ByteBuffer buffer = ByteBuffer.wrap(bytes);
+ channel.read(buffer);
+ writer.write(new String(bytes, 0, bytes.length, "UTF8"));
+ }
+ catch (FileNotFoundException e)
+ {
+ writer.print("No such file " + uri);
+ getPortletContext().log("Cannot find file " + uri, e);
+ }
+ finally
+ {
+ if (in != null)
+ {
+ in.close();
+ }
+ }
+ }
+
+ //
+ writer.close();
+}
+]]></programlisting>
+ </sect3>
+ <sect3>
+ <title>Hooking the portlet into the portal</title>
+ <imageobject>
+ <imagedata align="center"
fileref="images/content/fs2.png" format="png"/>
+ </imageobject>
+ <caption>Management portlet with
<emphasis>filesystem</emphasis> content type enabled</caption>
+ <para>Finally we need to make the portal aware of the fact that the
portlet can edit and interpret content. For that
+ we need a few descriptors. The <emphasis>portlet.xml</emphasis>
descriptor will define our portlet, the
+ <emphasis>portlet-instances.xml</emphasis> will create a single
instance of our portlet. The
+ <emphasis>web.xml</emphasis> descriptor will contain a servlet
context listener that will hook the content
+ type in the portal content type registry.</para>
+ <programlisting><![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<portlet-app
+
xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1...
+
http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
+ version="1.0">
+ ...
+ <portlet>
+ <description>File System Content Driven Portlet</description>
+ <portlet-name>FSContentDrivenPortlet</portlet-name>
+ <display-name>File System Content Driven Portlet</display-name>
+
<portlet-class>org.jboss.portal.core.portlet.test.FSContentDrivenPortlet</portlet-class>
+ <supports>
+ <mime-type>text/html</mime-type>
+ </supports>
+ <portlet-info>
+ <title>File Portlet</title>
+ <keywords>sample,test</keywords>
+ </portlet-info>
+ </portlet>
+ ...
+</portlet-app>
+]]></programlisting>
+ <para>The portlet.xml descriptor</para>
+ <programlisting><![CDATA[
+<deployments>
+ ...
+ <deployment>
+ <instance>
+ <instance-id>FSContentDrivenPortletInstance</instance-id>
+ <portlet-ref>FSContentDrivenPortlet</portlet-ref>
+ </instance>
+ </deployment>
+ ...
+</deployments
+]]></programlisting>
+ <para>The portlet-instances.xml descriptor</para>
+ <programlisting><![CDATA[
+<web-app>
+ ...
+ <context-param>
+ <param-name>org.jboss.portal.content_type</param-name>
+ <param-value>filesystem</param-value>
+ </context-param>
+ <context-param>
+ <param-name>org.jboss.portal.portlet_instance</param-name>
+ <param-value>FSContentDrivenPortletInstance</param-value>
+ </context-param>
+ <listener>
+
<listener-class>org.jboss.content.ContentTypeRegistration</listener-class>
+ </listener>
+ ...
+</web-app>
+]]></programlisting>
+ <para>The web.xml descriptor</para>
+ <warning>You don't need to add the listener class into your war
file. As it is provided by the portal
+ it will always be available.</warning>
+ </sect3>
+ </sect2>
+ </sect1>
+ <sect1>
+ <title>Configuring window content in deployment descriptor</title>
+ <para>How to create a portlet that will enable configuration of content at
runtime has been covered above, however
+ it is also possible to configure content in deployment descriptors. With our
previous example it would give
+ the following snippet placed in a <emphasis>*-portal.xml</emphasis>
file:
+ </para>
+ <programlisting><![CDATA[
+<window>
+<window-name>MyWindow</window-name>
+<content>
+ <content-type>filesystem</content-type>
+ <content-uri>/dir1/foo.txt</content-uri>
+</content>
+<region>center</region>
+<height>1</height>
+</window>
+]]></programlisting>
+ <imageobject>
+ <imagedata align="center"
fileref="images/content/fs3.png" format="png"/>
+ </imageobject>
+ <caption>Final effect - portal window with
FSContentDrivenPortlet</caption>
+ <note>How to configure CMS file this way is covered in the CMS chapter:
<xref linkend="configuration-cms_content"/></note>
+ </sect1>
+</chapter>