<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Integrating Stuff</title>
	<atom:link href="http://www.integratingstuff.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.integratingstuff.com</link>
	<description>A blog about integrating Java frameworks and third-party APIs with each other.</description>
	<lastBuildDate>Tue, 24 Jan 2012 07:12:20 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>How to create a Drupal 7 Module: introduction to Drupal modules, hooks, Drupal database API, Drupal File API and Drupal Form API</title>
		<link>http://www.integratingstuff.com/2012/01/23/how-to-create-a-drupal-7-module-introduction-to-drupal-modules-hooks-drupal-database-api-drupal-file-api-and-drupal-form-api/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-create-a-drupal-7-module-introduction-to-drupal-modules-hooks-drupal-database-api-drupal-file-api-and-drupal-form-api</link>
		<comments>http://www.integratingstuff.com/2012/01/23/how-to-create-a-drupal-7-module-introduction-to-drupal-modules-hooks-drupal-database-api-drupal-file-api-and-drupal-form-api/#comments</comments>
		<pubDate>Mon, 23 Jan 2012 03:00:18 +0000</pubDate>
		<dc:creator>Steffen Luypaert</dc:creator>
				<category><![CDATA[Php]]></category>
		<category><![CDATA[Drupal]]></category>

		<guid isPermaLink="false">http://www.integratingstuff.com/?p=561</guid>
		<description><![CDATA[<a href="http://drupal.org/">Drupal</a> is a free and open source content management system written in <a href="http://www.php.net/">PHP</a>. The standard release of Drupal, Drupal core, contains basic features typical to content management systems, such as menu management, page layout customization and content publishing. However, its main strength are its <a href="http://drupal.org/project/Modules">community-contributed addons</a>, known as modules, which make it possible to alter and extend Drupal's capabilities to one's wishes.
This article is about implementing such a module. <a href="http://drupal.org/node/1074360">There are other good tutorials on this</a>. This one is going to cover the specific case of creating a text file on the server with the content of certain nodes every time a node is created, updated or deleted. The tutorial delves into some of the most important Drupal APIs, such as the Database, File and Form APIs.
People reading this should have added a module to a Drupal installation before and be somewhat familiar with PHP.]]></description>
			<content:encoded><![CDATA[<p><a href="http://drupal.org/">Drupal</a> is a free and open source content management system written in <a href="http://www.php.net/">PHP</a>. The standard release of Drupal, Drupal core, contains basic features typical to content management systems, such as menu management, page layout customization and content publishing. However, its main strength are its <a href="http://drupal.org/project/Modules">community-contributed addons</a>, known as modules, which make it possible to alter and extend Drupal&#8217;s capabilities to one&#8217;s wishes.<br />
This article is about implementing such a module. <a href="http://drupal.org/node/1074360">There are other good tutorials on this</a>. This one is going to cover the specific case of creating a text file on the server with the content of certain nodes every time a node is created, updated or deleted. The tutorial delves into some of the most important Drupal APIs, such as the Database, File and Form APIs.<br />
People reading this should have added a module to a Drupal installation before and be somewhat familiar with PHP.</p>
<h2>1. Drupal Modules</h2>
<p>A module is a collection of php functions that link into Drupal, providing additional functionality to the Drupal installation.<br />
Because the module code executes within the Drupal context, it can access all the variables and structures and use all the functions of Drupal core.</p>
<h3>1.1. Choose a short name</h3>
<p>The first step in creating a module is to choose a short name for it. This short name will be used in all file and function names in your module. In this example, we will be using “nodes_to_text_file” as the short name. </p>
<p>Make sure your short name starts with a letter and only contains lower-case letters and underscores. Also make sure your module does not have the same short name as any theme you will be using.</p>
<h3>1.2. Create the directory</h3>
<p>In the modules directory of your Drupal 7 install, create a folder with your short name as its name. </p>
<h3>1.3 empty module file</h3>
<p>Create a nodes_to_text_file.module file in your nodes_to_text_file directory. It is to this file that we will add the functions that will hook into Drupal. In the next section, we will add the first function.<br />
Module files begin with the opening php tag. Add it to the module file:</p>
<pre class="is_code">
&lt;?php
</pre>
<p>However, omit the closing php tag ?&gt;. Omitting it is a convention, and including it might cause strange runtime behavior on certain server setups. </p>
<h3>1.4 Creating the info file</h3>
<p>Every module has its <a href="http://drupal.org/node/171205">info file</a>. It contains metadata about the module and hence, tells Drupal about the module. Without this file, the module will not appear in the module listing of your Drupal installation.</p>
<p>Create a nodes_to_text_file.info file in your nodes_to_text_file directory:</p>
<pre class="is_code">
name = Nodes To Text File
description = A module that creates a text file containing all content of certain types of nodes whenever a node of one of these types is added, edited or deleted
core = 7.x
</pre>
<p>The above 3 properties form the minimal setup: name sets the name of the module, description describes what the module does and core determines with which version of Drupal this module is meant to be used.</p>
<p>After adding the info file, the module should be visible in your module listing. Go to Modules and scroll down to the “Other” section. The Nodes To Text File module should be listed there. Enable it.</p>
<p><a href="http://www.integratingstuff.com/wp-content/uploads/2012/01/drupal_module_listing.png"><img src="http://www.integratingstuff.com/wp-content/uploads/2012/01/drupal_module_listing-300x154.png" alt="" title="Drupal Module Listing" width="300" height="154" class="aligncenter size-medium wp-image-567" /></a></p>
<h2>2. Module Hooks</h2>
<p>Hooks are the most important concept to grasp when implementing Drupal modules. Hooks provide the points at which you can insert your actions. They can be thought of as event listeners. An event such as deleting a node would trigger the hook &#8220;hook_node_delete&#8221;. In this case, Drupal will scan all modules for functions with the name mymodule_node_delete, where mymodule is the module&#8217;s name. If your module implements hook_node_delete, that function will always execute after a node gets deleted.</p>
<p>For a list of all available hooks in Drupal core, check <a href="http://api.drupal.org/api/drupal/includes--module.inc/group/hooks/7">the Drupal 7 hooks documentation</a>.</p>
<h3>2.1 Implementing the hook_help hook</h3>
<p>The first hook we are going to implement is <a href="http://api.drupal.org/api/drupal/modules--help--help.api.php/function/hook_help/7">hook_help</a>. This hook should always be implemented. It provides help and information about the module. To implement an hook, you need to replace &#8220;hook&#8221; in the hook name(f.e. hook_help) with your module&#8217;s short name and create a function in the .module file with that name.<br />
In this case, nodes_to_text_file_help:</p>
<pre class="is_code">
/**
 * Implements hook_help.
 *
 * Displays help and module information.
 *
 * @param path
 *   Which path of the site we're using to display help
 * @param arg
 *   Array that holds the current path as returned from arg() function
 */
function nodes_to_text_file_help($path, $arg) {
  switch ($path) {
    case "admin/help#nodes_to_text_file":
      return '&lt;p&gt;'.  t("A module that creates a text file containing all content of certain types of nodes whenever a node of one of these types is added, edited or deleted") .'&lt;/p&gt;';
      break;
  }
}
</pre>
<p>The path parameter contains the information about where the user is currently at. If the user is currently at the help section of our module, we return a paragraph that gives information about our module. Note that we use the Drupal <a href="http://api.drupal.org/api/drupal/includes--bootstrap.inc/function/t/7">t($string)</a> function, which translates the content if possible and which should be used on any string that is shown to the user.</p>
<p>Now, check Modules page of your Drupal site again. You should see a link &#8216;Help&#8217; on the right of your module. If you click it, you will see the paragraph returned by the nodes_to_text_file_help function of our module.</p>
<h3>2.2. Implementing the hooks hook_node_insert, hook_node_update and hook_node_delete</h3>
<p>We will now implement the hooks <a href="http://api.drupal.org/api/drupal/modules--node--node.api.php/function/hook_node_insert/7">hook_node_insert</a>, <a href="http://api.drupal.org/api/drupal/modules--node--node.api.php/function/hook_node_update/7">hook_node_update</a> and <a href="http://api.drupal.org/api/drupal/modules--node--node.api.php/function/hook_node_delete/7">hook_node_delete</a>.<br />
All these hooks will call the same function nodes_to_text_file_write_file($node):</p>
<pre class="is_code">
/**
 * Implements hook_node_delete.
 */
function nodes_to_text_file_node_delete($node){
	nodes_to_text_file_write_file($node);
}

/**
 * Implements hook_node_update.
 */
function nodes_to_text_file_node_update($node){
	nodes_to_text_file_write_file($node);
}

/**
 * Implements hook_node_insert.
 */
function nodes_to_text_file_node_insert($node){
	nodes_to_text_file_write_file($node);
}
</pre>
<p>For now, add the following implementation of the function:</p>
<pre class="is_code">
function nodes_to_text_file_write_file($node){
	watchdog('We should write the file again!','Info Message');
}
</pre>
<p><a href="http://api.drupal.org/api/drupal/includes--bootstrap.inc/function/watchdog/7">watchdog</a> is a useful function to debug hooks. They will be added to your Drupal server logging at Reports&gt;Recent log messages:</p>
<p>&lt;watchdog screenshot&gt;</p>
<h2>3. Database API</h2>
<p>For this app, we will need two queries. One that fetches all the published nodes of the given content types of which the user is the owner:</p>
<pre class="is_code">
/**
 * Custom content function.
 *
 * Retrieve data from database
 *
 * @return
 *   A result set of the targeted nodes.
 */
function nodes_to_text_file_contents($contentTypes){
  //Use Database db_select API to retrieve nodes.
  global $user;
  $uid = $user-&gt;uid;

  $queryResult = db_select('node', 'n')
    -&gt;fields('n', array('nid', 'title', 'created'))
    -&gt;condition('uid', $uid) //owner is current user.
    -&gt;condition('status', 1) //Published.
    -&gt;condition('type', $contentTypes, 'in') //Node type is in $contentTypes
    -&gt;orderBy('created', 'DESC') //Most recent first.
    -&gt;execute();
  return $queryResult;
}
</pre>
<p>This query uses the new(starting from Drupal 7) <a href="http://api.drupal.org/api/drupal/includes--database--database.inc/function/db_query/7">db_select</a> function of the Database API. Other than the old <a href="http://api.drupal.org/api/drupal/includes--database--database.inc/function/db_query/7">db_query</a> function, db_select makes it possible to construct the query in an object oriented matter.</p>
<p><strong>db_select</strong> maps to a sql select statement(as do <a href="http://api.drupal.org/api/drupal/includes--database--database.inc/function/db_insert/7">db_insert</a>, <a href="http://api.drupal.org/api/drupal/includes--database--database.inc/function/db_update/7">db_update</a> and <a href="http://api.drupal.org/api/drupal/includes--database--database.inc/function/db_delete/7">db_delete</a> map to their sql equivalents). It takes a table name(f.e. &#8216;node&#8217;) and an alias (f.e. &#8216;n&#8217;) as its arguments. This comes down to:</p>
<pre class="is_code">
select * from node as n
</pre>
<p><strong>fields</strong> selects the fields listed in the array in its second argument on the alias n:</p>
<pre class="is_code">
select nid, title, created from node as n
</pre>
<p><strong>condition</strong> adds a condition to the query. The first is the field, the second the value, the third the operator:</p>
<pre class="is_code">
select nid, title, created from node as n where published = 1 and type in ('article','page') and uid = 1
</pre>
<p>if the values in the $contentTypes array are &#8216;article&#8217; and &#8216;page&#8217; and user Steffen has uid 1. Note that the “global $user” statement gets the global $user variable, which contains all information about the user that is currently logged in.</p>
<p><strong>orderBy</strong> then sort the results according to the field in its first argument:</p>
<pre class="is_code">
select nid, title, created from node as n where published = 1 and type in ('article','page') and uid = 1 order by created asc
</pre>
<p>The <strong>execute</strong> method then compiles and runs the query and returns the result.</p>
<p>The other query we need – one that fetches all content types of the core <a href="http://api.drupal.org/api/drupal/modules--node--node.module/7">Node Module</a> – uses the same API and is quite simular:</p>
<pre code="is_code">
/**
 * Function that retrieves the content types from the database.
 *
 * Retrieve content types from database
 *
 * @return
 *   A result set of content types.
 */
function nodes_to_text_file_retrieve_content_types(){
  $queryResult = db_select('node_type', 'nt')
    -&gt;fields('nt', array('type', 'name'))
    -&gt;condition('module', 'node') //only node content types
    -&gt;orderBy('type', 'ASC')
    -&gt;execute();
  return $queryResult;
}
</pre>
<h2>4. Drupal File API</h2>
<p>We are now implementing our nodes_to_text_file_write_file function that is called by the node_insert, node_update and node_delete hooks. We briefly discuss the part in which we determine whether the file should be written and in which we fetch the contents first.</p>
<pre class="is_code">
/**
 * Custom write file function.
 *
 * Creates the text file and stores it on the server.
 */
function nodes_to_text_file_write_file($node){
	$contentTypes = variable_get('nodes_to_text_file_content_types', array());
	$nodeType = $node-&gt;type;

	$elementFound = false;
	foreach ($contentTypes as &#038;$value) {
    	if ($nodeType === $value){
    		$elementFound = true;
    		break;
    	}
	}

	if ($elementFound){
    	$queryResult = nodes_to_text_file_contents($contentTypes);

    	global $user;
    	$username = $user-&gt;name;

   	...
}
</pre>
<p>First, we fetch the node content types which should be included in the file using <a href="http://api.drupal.org/api/drupal/includes--bootstrap.inc/function/variable_get/7">variable_get($name,$default)</a> As we will see in the next section, <a href="http://api.drupal.org/api/drupal/includes--bootstrap.inc/function/variable_set/7">variable_set($name,$value)</a> – typically invoked in a module configuration form &#8211; sets Drupal system wide variables, which can then be fetched by variable_get.<br />
We then match the node type of the node that was inserted, updated or deleted with the array of content types. If a match is found($elementFound), execution continues. We fetch the nodes using the query of the previous section and get a reference to the username of the currently logged in user.</p>
<p>Then – where the &#8230; dots are in the above function &#8211; we add the following and write out the fetched content to a file:</p>
<pre class="is_code">
	if (! file_prepare_directory(file_build_uri($username))){
    		drupal_mkdir(file_build_uri($username));
    	}

    	$filename = file_build_uri($username . '/content.txt');
    	$fh = fopen($filename, 'w') or die("can't open file");

   	 	while($record = $queryResult-&gt;fetchAssoc()) {
    		$stringData = $record['nid'] . ' - ' . $record['title'] . ' - ' . $record['created'];
    		fwrite($fh, $stringData."\n");
    	} 

    	fclose($fh);
</pre>
<p>We write the fetched contents to a context.txt file in the default Drupal public files location, in a folder with the username as its name. If this folder does not exist yet, we create it first:</p>
<pre class="is_code">
if (! file_prepare_directory(file_build_uri($username))){
   drupal_mkdir(file_build_uri($username));
}
</pre>
<p>Then we construct the filename:</p>
<pre class="is_code">
$filename = file_build_uri($username . '/content.txt');
</pre>
<p>If the username is “Steffen”, then the filename will be:<br />
&lt;drupal installation location&gt;/sites/default/files/steffen/content.txt</p>
<p>Then we open the file for writing and iterate over the queryResult. We print out the nid, the title and the created timestamp of every record to a new line in the file. We then close the file again:</p>
<pre class="is_code">
   $fh = fopen($filename, 'w') or die("can't open file");

   while($record = $queryResult-&gt;fetchAssoc()) {
      $stringData = $record['nid'] . ' - ' . $record['title'] . ' - ' . $record['created'];
      fwrite($fh, $stringData."\n");
   } 

   fclose($fh);
</pre>
<p>We are using some functions of the Drupal <a href="http://api.drupal.org/api/drupal/includes--file.inc/group/file/7">File API</a> here:<br />
<a href="http://api.drupal.org/api/drupal/includes--file.inc/function/file_build_uri/7">file_build_uri</a>: This function constructs, given a relative path, a URI into Drupal&#8217;s default files location(this is usually something like &#8216;sites/default/files/&#8217;).<br />
<a href="http://api.drupal.org/api/drupal/includes--file.inc/function/file_prepare_directory/7">file_prepare_directory</a>: This function checks that the directory exists and is writable.<br />
<a href="http://api.drupal.org/api/drupal/includes--file.inc/function/drupal_mkdir/7">drupal_mkdir</a>: This function creates a directory using Drupal&#8217;s default mode.</p>
<p>To respectively open the file for writing, write to it and close it again, we use the php functions <a href="http://php.net/manual/function.fopen.php">fopen</a>, <a href="http://php.net/manual/function.fwrite.php">fwrite</a> and <a href="http://php.net/manual/function.fclose.php">fclose</a>. </p>
<h2>5. Drupal Form API</h2>
<p>Our module is implemented now. However, we want to be able to configure the node content types for which the file should be written. We already saw that we were fetching this array of content types like this:</p>
<pre class="is_code">
$contentTypes = variable_get('nodes_to_text_file_content_types', array());
</pre>
<p>What remains is to explain how we can set this array of content types first through a module configuration form.</p>
<p>We will implement <a href="http://api.drupal.org/api/drupal/modules--system--system.api.php/function/hook_menu/7">hook_menu</a> so we get a configuration form at our module at the relative path <em>admin/config/content/nodes_to_text_file</em>:</p>
<pre class="is_code">
/**
 * Implements hook_menu().
 */
function nodes_to_text_file_menu() {
  $items = array();  

  $items['admin/config/content/nodes_to_text_file'] = array(
    'title' =&gt; 'Nodes To Text File',
    'description' =&gt; 'Configuration for Nodes To Text File module',
    'page callback' =&gt; 'drupal_get_form',
    'page arguments' =&gt; array('nodes_to_text_file_form'),
    'access arguments' =&gt; array('access administration pages'),
    'type' =&gt; MENU_NORMAL_ITEM
  );

  return $items;
}
</pre>
<p>Using the hook_menu hook, modules register paths to define how URL requests are handled. In this case, we are setting a path that makes a configuration form available from the Drupal Configuration page:</p>
<p><a href="http://www.integratingstuff.com/wp-content/uploads/2012/01/drupal_module_menu.png"><img src="http://www.integratingstuff.com/wp-content/uploads/2012/01/drupal_module_menu-300x98.png" alt="" title="Drupal Module Menu" width="300" height="98" class="aligncenter size-medium wp-image-568" /></a></p>
<p>We passed the form already to the array of the &#8216;page arguments&#8217; property of the link, but we didnt define it yet. We are doing so now:</p>
<pre class="is_code">
/**
 * Form function, called by drupal_get_form()
 * in nodes_to_text_file_menu().
 */
function nodes_to_text_file_form($form, &#038;$form_state) {
	$queryResult = nodes_to_text_file_retrieve_content_types();

	$options = array();
	while($record = $queryResult-&gt;fetchAssoc()) {
    	$options[$record['type']] = t($record['name']);
    } 

  	$form['nodes_to_text_file_content_types'] = array(
    	'#type' =&gt; 'checkboxes',
    	'#title' =&gt; t('Content types to include'),
    	'#default_value' =&gt; variable_get('nodes_to_text_file_content_types', array()),
    	'#description' =&gt; t('The content types to include.'),
    	'#options' =&gt; $options
  	);

  return system_settings_form($form);
}
</pre>
<p>First we are creating the array of possible content types so we can use it later to generate the necessary checkboxes on the form:</p>
<pre class="is_code">
$queryResult = nodes_to_text_file_retrieve_content_types();

	$options = array();
	while($record = $queryResult-&gt;fetchAssoc()) {
    	$options[$record['type']] = t($record['name']);
    }
</pre>
<p>In Drupal, forms should be constructed using the <a href="http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/7">Form API</a>. We are using it to construct our form:</p>
<pre class="is_code">
$form['nodes_to_text_file_content_types'] = array(
    	'#type' =&gt; 'checkboxes',
    	'#title' =&gt; t('Content types to include'),
    	'#default_value' =&gt; variable_get('nodes_to_text_file_content_types', array()),
    	'#description' =&gt; t('The content types to include.'),
    	'#options' =&gt; $options
  	);
</pre>
<p>after which we return the form. Note that the Form API is pretty straightforward. It is generally enough to consult the API page to know what types of fields are available and which properties are available and which ones are required.</p>
<h2>6. The entire module file for reference</h2>
<pre class="is_code">
&lt;?php
/**
 * Implements hook_help.
 *
 * Displays help and module information.
 *
 * @param path
 *   Which path of the site we're using to display help
 * @param arg
 *   Array that holds the current path as returned from arg() function
 */
function nodes_to_text_file_help($path, $arg) {
  switch ($path) {
    case "admin/help#nodes_to_text_file":
      return '&lt;p&gt;'.  t("A module that creates a text file containing all content of certain types of nodes whenever a node of one of these types is added, edited or deleted") .'&lt;/p&gt;';
      break;
  }
}

/**
 * Implements hook_node_delete.
 */
function nodes_to_text_file_node_delete($node){
	nodes_to_text_file_write_file($node);
}

/**
 * Implements hook_node_update.
 */
function nodes_to_text_file_node_update($node){
	nodes_to_text_file_write_file($node);
}

/**
 * Implements hook_node_insert.
 */
function nodes_to_text_file_node_insert($node){
	nodes_to_text_file_write_file($node);
}

/**
 * Custom write file function.
 *
 * Creates the text file and stores it on the server.
 */
function nodes_to_text_file_write_file($node){
	$contentTypes = variable_get('nodes_to_text_file_content_types', array());
	$nodeType = $node-&gt;type;

	$elementFound = false;
	foreach ($contentTypes as &#038;$value) {
    	if ($nodeType === $value){
    		$elementFound = true;
    		break;
    	}
	}

	if ($elementFound){
    	$queryResult = nodes_to_text_file_contents($contentTypes);

    	global $user;
    	$username = $user-&gt;name;

   	 	if (! file_prepare_directory(file_build_uri($username))){
    		drupal_mkdir(file_build_uri($username));
    	}

    	$filename = file_build_uri($username . '/content.txt');
    	$fh = fopen($filename, 'w') or die("can't open file");

   	 	while($record = $queryResult-&gt;fetchAssoc()) {
    		$stringData = $record['nid'] . ' - ' . $record['title'] . ' - ' . $record['created'];
    		fwrite($fh, $stringData."\n");
    	} 

    	fclose($fh);
	}
}

/**
 * Custom content function.
 *
 * Retrieve data from database
 *
 * @return
 *   A result set of the targeted nodes.
 */
function nodes_to_text_file_contents($contentTypes){
  //Use Database db_select API to retrieve nodes.
  global $user;
  $uid = $user-&gt;uid;

  $queryResult = db_select('node', 'n')
    -&gt;fields('n', array('nid', 'title', 'created'))
    -&gt;condition('uid', $uid) //owner is current user.
    -&gt;condition('status', 1) //Published.
    -&gt;condition('type', $contentTypes, 'in') //Node type is in $contentTypes
    -&gt;orderBy('created', 'DESC') //Most recent first.
    -&gt;execute();
  return $queryResult;
}

/**
 * Function that retrieves the content types from the database.
 *
 * Retrieve content types from database
 *
 * @return
 *   A result set of content types.
 */
function nodes_to_text_file_retrieve_content_types(){
  $queryResult = db_select('node_type', 'nt')
    -&gt;fields('nt', array('type', 'name'))
    -&gt;condition('module', 'node') //only node content types
    -&gt;orderBy('type', 'ASC')
    -&gt;execute();
  return $queryResult;
}

/**
 * Form function, called by drupal_get_form()
 * in nodes_to_text_file_menu().
 */
function nodes_to_text_file_form($form, &#038;$form_state) {
	$queryResult = nodes_to_text_file_retrieve_content_types();

	$options = array();
	while($record = $queryResult-&gt;fetchAssoc()) {
    	$options[$record['type']] = t($record['name']);
    } 

  	$form['nodes_to_text_file_content_types'] = array(
    	'#type' =&gt; 'checkboxes',
    	'#title' =&gt; t('Content types to include'),
    	'#default_value' =&gt; variable_get('nodes_to_text_file_content_types', array()),
    	'#description' =&gt; t('The content types to include.'),
    	'#options' =&gt; $options
  	);

  return system_settings_form($form);
}

/**
 * Implements hook_menu().
 */
function nodes_to_text_file_menu() {
  $items = array();  

  $items['admin/config/content/nodes_to_text_file'] = array(
    'title' =&gt; 'Nodes To Text File',
    'description' =&gt; 'Configuration for Nodes To Text File module',
    'page callback' =&gt; 'drupal_get_form',
    'page arguments' =&gt; array('nodes_to_text_file_form'),
    'access arguments' =&gt; array('access administration pages'),
    'type' =&gt; MENU_NORMAL_ITEM
  );

  return $items;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.integratingstuff.com/2012/01/23/how-to-create-a-drupal-7-module-introduction-to-drupal-modules-hooks-drupal-database-api-drupal-file-api-and-drupal-form-api/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Migrating the contents of a MySql database from a Windows/Mac server to a Linux server</title>
		<link>http://www.integratingstuff.com/2011/12/30/migrating-the-contents-of-a-mysql-database-from-a-windowsmac-server-to-a-linux-server/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=migrating-the-contents-of-a-mysql-database-from-a-windowsmac-server-to-a-linux-server</link>
		<comments>http://www.integratingstuff.com/2011/12/30/migrating-the-contents-of-a-mysql-database-from-a-windowsmac-server-to-a-linux-server/#comments</comments>
		<pubDate>Fri, 30 Dec 2011 17:21:15 +0000</pubDate>
		<dc:creator>Steffen Luypaert</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Migrating databases]]></category>
		<category><![CDATA[MySql]]></category>

		<guid isPermaLink="false">http://www.integratingstuff.com/?p=535</guid>
		<description><![CDATA[It is easy to make a backup of a MySql database and import the export again into another MySql database. However, when the backup is made on a Windows or Mac machine and is then used on a Linux machine, it is possible problems arise due to the fact that filenames/tablenames are case sensitive on the Linux OS but the export on Windows/Mac does not preserve case. <a href="http://stackoverflow.com/questions/6248735/how-to-force-mysql-to-use-case-sensitive-table-names-in-windows">Some propose other solutions – passing a parameter when starting the database or upgrading to a more recent version of MySql - to solve this problem</a>. However, because I had no control over the database to take the backup from, I had to use the below method which just takes the backup file and a text file in which all tablenames are present with their case set right, and then uses the second file to simply replace all lowercase occurences in the first file with the version with the proper case.]]></description>
			<content:encoded><![CDATA[<p>It is easy to make a backup of a MySql database and import the export again into another MySql database. However, when the backup is made on a Windows or Mac machine and is then used on a Linux machine, it is possible problems arise due to the fact that filenames/tablenames are case sensitive on the Linux OS but the export on Windows/Mac does not preserve case. <a href="http://stackoverflow.com/questions/6248735/how-to-force-mysql-to-use-case-sensitive-table-names-in-windows">Some propose other solutions – passing a parameter when starting the database or upgrading to a more recent version of MySql &#8211; to solve this problem</a>. However, because I had no control over the database to take the backup from, I had to use the below method which just takes the backup file and a text file in which all tablenames are present with their case set right, and then uses the second file to simply replace all lowercase occurences in the first file with the version with the proper case.</p>
<h2>Making a backup of a Mysql database and restoring it</h2>
<p>Making a backup of a MySql database is straightforward. I am giving the statements below for a Mac, but on Windows the same mysqldump and mysql commands can be used from the bin folder of the MySql installation.</p>
<p>First navigate to your MySql installation folder. This will probably be something like:</p>
<pre class="is_code">
cd /usr/local/mysql
</pre>
<p>And then run mysqldump:</p>
<pre class="is_code">
sudo ./bin/mysqldump -u &lt;username&gt; -p &lt;password&gt; testdatabase &gt; /development/testdatabase.sql
</pre>
<p>where &lt;username&gt; is your mysql username and &lt;password&gt; is your mysql password.</p>
<p>This will generate a sql script in the development folder that is able to recreate all tables and indexes and reinsert all data that are available in the testdatabase, by running the following command on the target server:</p>
<pre class="is_code">
sudo ./bin/mysql testdatabase &lt; /development/testdatabase.sql
</pre>
<p>assuming the testdatabase.sql was first sent to the development folder on the target server by using the <a href="http://linux.about.com/od/commands/l/blcmdl1_scp.htm">scp command</a> or some graphical client.</p>
<p>However, when running the above mysqldump command on Mac or Windows, all tablenames are stored in lowercase in the target sql script file by default. When it is then imported on a Linux server, all tablenames are in lowercase as well.</p>
<p>This is a problem, since the case of the tablenames matters on Linux machines. It is possible that applications – written in PHP, Java or something else &#8211; that will make use of the database expect some or all of these tablenames to be in upper case or have its first letter capitalized. These applications will then not run correctly.</p>
<h2>Case sensitivity: the problem when migrating a MySql database from Windows/Mac to Linux</h2>
<p>Hence, it is necessary the cases are preserved in the generated sql script. <a href="http://stackoverflow.com/questions/2992079/mysqldump-problem-with-case-sensitivity-win-linux">The answer to this Stackoverflow question</a> suggests to restart the mysql database on the Mac/windows server with the lower_case_table_names parameter set to 0. For more information about this, please check <a href="http://dev.mysql.com/doc/refman/5.1/en/identifier-case-sensitivity.html">the Mysql documentation about the lower_case_table_names parameter</a>.</p>
<p>However, you might not have the permissions to take the database offline, restart it with this parameter and then run mysqldump again. You are then stuck with the script with only lower case table names. I think the only option is then to edit the script and replace all lower case occurences with the version with the proper case.</p>
<p>The method that I describe below is just a quick way to do this using Java.</p>
<h3>Getting the list of tablenames with their case right</h3>
<p>Most applications – such as Java web applications (using JPA/Hibernate for example), and Drupal and WordPress installations &#8211; are able to automatically create the database tables they need if they are missing. Hence, you can run the application once and make sure the tables are generated. You then open a MySql terminal. </p>
<p>On the Linux machine, you would run the following from the mysql installation bin folder:</p>
<pre class="is_code">
mysql --user=&lt;user&gt; --password=&lt;password&gt;
</pre>
<p>After which you will get the mysql terminal.<br />
Assuming the tables were already generated by the web application in the testdatabase database, you run:</p>
<pre class="is_code">
mysql&gt; show tables from testdatabase;
</pre>
<p>Which in my case renders:</p>
<pre class="is_code">
+--------------------------------+
| Tables_in_testdatabase         |
+--------------------------------+
| Account_                       |
| Address                        |
| AnnouncementsDelivery          |
| AnnouncementsEntry             |
| AnnouncementsFlag              |
| AssetCategory                  |
| AssetCategoryProperty          |
| AssetEntries_AssetCategories   |
| AssetEntries_AssetTags         |
| AssetEntry                     |
| AssetLink                      |
| AssetTag                       |
| AssetTagProperty               |
| AssetTagStats                  |
| AssetVocabulary                |
| BlogsEntry                     |
| BlogsStatsUser                 |
| BookmarksEntry                 |
| BookmarksFolder                |
| BrowserTracker                 |
| CalEvent                       |
| Chat_Entry                     |
| Chat_Status                    |
| ClassName_                     |
| ClusterGroup                   |
| Company                        |
| Contact_                       |
| Counter                        |
| Country                        |
| CyrusUser                      |
| CyrusVirtual                   |
| DLFileEntry                    |
| DLFileRank                     |
| DLFileShortcut                 |
| DLFileVersion                  |
| DLFolder                       |
| EmailAddress                   |
| ExpandoColumn                  |
| ExpandoRow                     |
| ExpandoTable                   |
| ExpandoValue                   |
| Group_                         |
| Groups_Orgs                    |
| Groups_Permissions             |
| Groups_Roles                   |
| Groups_UserGroups              |
| IGFolder                       |
| IGImage                        |
| Image                          |
| JournalArticle                 |
| JournalArticleImage            |
| JournalArticleResource         |
| JournalContentSearch           |
| JournalFeed                    |
| JournalStructure               |
| JournalTemplate                |
| Layout                         |
| LayoutPrototype                |
| LayoutSet                      |
| LayoutSetPrototype             |
| ListType                       |
| Lock_                          |
| MBBan                          |
| MBCategory                     |
| MBDiscussion                   |
| MBMailingList                  |
| MBMessage                      |
| MBMessageFlag                  |
| MBStatsUser                    |
| MBThread                       |
| Mail_Account                   |
| Mail_Attachment                |
| Mail_Folder                    |
| Mail_Message                   |
| MembershipRequest              |
| OpenSocial_Gadget              |
| OrgGroupPermission             |
| OrgGroupRole                   |
| OrgLabor                       |
| Organization_                  |
| PasswordPolicy                 |
| PasswordPolicyRel              |
| PasswordTracker                |
| Permission_                    |
| Phone                          |
| PluginSetting                  |
| PollsChoice                    |
| PollsQuestion                  |
| PollsVote                      |
| Portlet                        |
| PortletItem                    |
| PortletPreferences             |
| QUARTZ_BLOB_TRIGGERS           |
| QUARTZ_CALENDARS               |
| QUARTZ_CRON_TRIGGERS           |
| QUARTZ_FIRED_TRIGGERS          |
| QUARTZ_JOB_DETAILS             |
| QUARTZ_JOB_LISTENERS           |
| QUARTZ_LOCKS                   |
| QUARTZ_PAUSED_TRIGGER_GRPS     |
| QUARTZ_SCHEDULER_STATE         |
| QUARTZ_SIMPLE_TRIGGERS         |
| QUARTZ_TRIGGERS                |
| QUARTZ_TRIGGER_LISTENERS       |
| RatingsEntry                   |
| RatingsStats                   |
| Region                         |
| Release_                       |
| ResourceAction                 |
| ResourceCode                   |
| ResourcePermission             |
| Resource_                      |
| Role_                          |
| Roles_Permissions              |
| SCFrameworkVersi_SCProductVers |
| SCFrameworkVersion             |
| SCLicense                      |
| SCLicenses_SCProductEntries    |
| SCProductEntry                 |
| SCProductScreenshot            |
| SCProductVersion               |
| SN_MeetupsEntry                |
| SN_MeetupsRegistration         |
| SN_WallEntry                   |
| ServiceComponent               |
| Shard                          |
| ShoppingCart                   |
| ShoppingCategory               |
| ShoppingCoupon                 |
| ShoppingItem                   |
| ShoppingItemField              |
| ShoppingItemPrice              |
| ShoppingOrder                  |
| ShoppingOrderItem              |
| SocialActivity                 |
| SocialEquityAssetEntry         |
| SocialEquityGroupSetting       |
| SocialEquityHistory            |
| SocialEquityLog                |
| SocialEquitySetting            |
| SocialEquityUser               |
| SocialRelation                 |
| SocialRequest                  |
| Subscription                   |
| TasksProposal                  |
| TasksReview                    |
| Team                           |
| Ticket                         |
| UserGroup                      |
| UserGroupGroupRole             |
| UserGroupRole                  |
| UserIdMapper                   |
| UserTracker                    |
| UserTrackerPath                |
| User_                          |
| Users_Groups                   |
| Users_Orgs                     |
| Users_Permissions              |
| Users_Roles                    |
| Users_Teams                    |
| Users_UserGroups               |
| Vocabulary                     |
| WSRP_WSRPConsumer              |
| WSRP_WSRPConsumerPortlet       |
| WSRP_WSRPProducer              |
| WebDAVProps                    |
| Website                        |
| WikiNode                       |
| WikiPage                       |
| WikiPageResource               |
| WorkflowDefinitionLink         |
| WorkflowInstanceLink           |
| blocked_user                   |
| deleted_message                |
| private_message                |
| read_message                   |
+--------------------------------+
176 rows in set (0.55 sec)
</pre>
<p>The above is a list of tablenames for the Liferay 6.0 portal and migrating these tables is a case in which I actually used the method described here. I stripped the list of all pipes(|) and saved the list of tablenames into a txt file, one tablename per line.</p>
<p>If you cannot generate the list of tablenames with the right case automatically like above, you will need to manually create this file, severely slowing up the method used in this tutorial.</p>
<h3>Setting the case right with some Java code</h3>
<p>The below Java class takes the testdatabase.sql file and the tablenames.txt file as input and writes the sql script with the cases set right to a testdatabase-converted.sql file:</p>
<pre class="is_code">
public class SetCaseRight {

	private static List&lt;String&gt; readFile(String fileName){
		try{
			List&lt;String&gt; lines = new ArrayList&lt;String&gt;();
			FileInputStream fstream = new FileInputStream(fileName);
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			while ((strLine = br.readLine()) != null)   {
				lines.add(strLine);
			}
			in.close();
			return lines;
		}catch (Exception e){
			e.printStackTrace();
			return null;
		}
	}

	private static String readFileAsString(String fileName){
        try {
			StringBuffer fileData = new StringBuffer(1000);
			BufferedReader reader = new BufferedReader(new FileReader(fileName));
			char[] buf = new char[1024];
			int numRead=0;
			while((numRead=reader.read(buf)) != -1){
			    String readData = String.valueOf(buf, 0, numRead);
			    fileData.append(readData);
			    buf = new char[1024];
			}
			reader.close();
			return fileData.toString();
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
    }

	private static void writeStringToFile(String s, String fileName){
		try {
			BufferedWriter out = new BufferedWriter(new FileWriter(fileName));
			out.write(s);
			out.close();
		}
		catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args){
		String sqlSourceFile;
		String tableNamesTxtFile;
		String sqlTargetFile;
		if (args.length &gt;= 1){ sqlSourceFile = args[0];} else { sqlSourceFile = "testdatabase.sql";}
		if (args.length &gt;= 2){ tableNamesTxtFile = args[1];} else { tableNamesTxtFile = "tablenames.txt";}
		if (args.length &gt;= 3){ sqlTargetFile = args[2];} else { sqlTargetFile = "testdatabase-converted.sql";}
		String sql = getSql(sqlSourceFile);
		for (String tableName: readTableNames(tableNamesTxtFile)){
			sql = sql.replaceAll("`(?i)" + tableName.trim()+"`", "`"+tableName.trim()+"`");
		}
		writeStringToFile(sql, sqlTargetFile);
	}

	public static String getSql(String sqlSourceFile){
		return readFileAsString(sqlSourceFile);
	}

	public static List&lt;String&gt; readTableNames(String tableNamesTxtFile){
		return readFile(tableNamesTxtFile);
	}
}
</pre>
<p>It is also possible to run the above class command line – without having to compile it yourself &#8211; <a href="http://www.integratingstuff.com/wp-content/uploads/2012/01/SetCaseRight.class">by using this SetCaseRight class file</a>:</p>
<pre class="is_code">
java SetCaseRight testdatabase.sql tablenames.txt testdatabase-converted.sql
</pre>
<p>in a folder where java is on the PATH and where the first argument is the sqlTargetFile, the second argument is the tableNamesTxtFile and the third argument is the sqlTargetFile.</p>
<p>The resulting sql file is then ready to be imported on the Linux server.</p>
<div class="is_note">
Note: The above class is not perfect. It is possible it replaces too many parts of the sql file, such as columnnames that are matching one of the tablenames present in the text file. This should not cause a problem, since column names are case insensitive as far as I know, but if it is a problem, you will need to modify the above Java class and make the string replacement a bit smarter.
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.integratingstuff.com/2011/12/30/migrating-the-contents-of-a-mysql-database-from-a-windowsmac-server-to-a-linux-server/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Styling Liferay: creating a Liferay Theme</title>
		<link>http://www.integratingstuff.com/2011/11/30/styling-liferay-creating-a-liferay-theme/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=styling-liferay-creating-a-liferay-theme</link>
		<comments>http://www.integratingstuff.com/2011/11/30/styling-liferay-creating-a-liferay-theme/#comments</comments>
		<pubDate>Wed, 30 Nov 2011 18:16:28 +0000</pubDate>
		<dc:creator>Steffen Luypaert</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Liferay]]></category>
		<category><![CDATA[Portals]]></category>

		<guid isPermaLink="false">http://www.integratingstuff.com/?p=501</guid>
		<description><![CDATA[In most Liferay projects, a custom Liferay theme needs to be developed and this article explains to developers how to do this.
Note that the <a href="http://www.liferay.com/documentation/liferay-portal/6.0/development/-/ai/creating-liferay-them-5">Liferay documentation already has some nice pages on creating Liferay themes</a>, but we are taking a more hands on approach.]]></description>
			<content:encoded><![CDATA[<p>In most Liferay projects, a custom Liferay theme needs to be developed and this article explains to developers how to do this.<br />
Note that the <a href="http://www.liferay.com/documentation/liferay-portal/6.0/development/-/ai/creating-liferay-them-5">Liferay documentation already has some nice pages on creating Liferay themes</a>, but we are taking a more hands on approach.</p>
<h2>Setting up the theme project</h2>
<p>We are assuming you will be using Eclipse, but will always tell what is going on under the hood, so other IDE(or commandline!) guys should also be able to follow.</p>
<p>You should start out by downloading the Liferay Plugins SDK from the bottom of <a href="http://www.liferay.com/downloads/liferay-portal/additional-files">the Liferay additional files page</a>.<br />
Eclipse users should also install the Liferay IDE Eclipse Plugin and add a Liferay Server(and runtime) after. More info on how to do this can be found in <a href="http://www.liferay.com/community/wiki/-/wiki/Main/Liferay+IDE+Getting+Started+Tutorial">the Liferay documentation on installing the Liferay IDE</a> or at this very own blog, in the last part of <a href="http://www.integratingstuff.com/2011/05/29/getting-started-with-portals-writing-a-portlet-with-spring-portlet-mvc-and-deploying-it-to-a-portal/">the getting started with portals and Spring Portlet MVC post</a>.</p>
<p>If you were already working with Liferay, you probably had these installed already anyway and the only thing you need to do then, is start your Liferay server.</p>
<p>Then, within Eclipse, choose New&gt;Project&gt;Liferay&gt;Liferay Project and then check Theme in the box.</p>
<p><a href="http://www.integratingstuff.com/wp-content/uploads/2011/11/create-liferay-theme.png"><img src="http://www.integratingstuff.com/wp-content/uploads/2011/11/create-liferay-theme.png" alt="Creating a Liferay Theme project in Eclipse" title="Creating a Liferay Theme project in Eclipse" width="586" height="657" class="alignnone size-full wp-image-504" /></a></p>
<p>What the theme plugin does is run a script from the &lt;liferay-sdk-home&gt;/themes folder that generates a Liferay theme project for you, in your Eclipse workspace.<br />
You can run the script manually by entering </p>
<pre class="is_code">
./create.sh integrating-stuff "Integrating Stuff"
</pre>
<p>or </p>
<pre class="is_code">
create.bat integrating-stuff "Integrating Stuff"
</pre>
<p>in a terminal from the said &lt;sdkhome&gt;/themes folder.</p>
<p>The Liferay IDE also takes a look at your Liferay runtime and copies a bunch of files to the docroot folder of your project. </p>
<div class="is_note">
Note: That is, if you have the &#8220;Build automatically&#8221; feature on in Eclipse. The copying of these files is done by the Ant build script included in the project and this script is ran automatically when the project gets built.
</div>
<p>These files represent the _styled theme, a very basic Liferay theme, which can be found in the portal source in the portal-web/docroot/html/themes folder or in the docroot/html/themes folder of your Liferay install.<br />
<a href="http://www.liferay.com/documentation/liferay-portal/6.0/development/-/ai/theme-inheritance">As the Liferay documentation indicates</a>, almost all themes will use the “_styled” theme as a base. If you want to start from scratch, the parent of your theme would be the “_unstyled” theme. Inheriting from “_unstyled” would give you quite a bit more work but would give you full control of the theme.</p>
<div class="is_note">
Note: _styled and _unstyled are very basic, abstract parent themes. They are not production ready. The default Liferay theme &#8211; “Classic” &#8211; inherits from _styled and is the official example of a production ready theme.
</div>
<p>Any of the files that were copied to your docroot folder can be overridden by your theme by placing them in your _diffs folder. In other words, the _diffs folder will contain all files that differ from your parent theme.</p>
<p>Note that, as already indicated above, the “_styled” theme is not production ready. Since our _diffs folder is still empty, our theme – which is currently not overriding anything and is an exact representation of the “_styled” theme &#8211; would not look that great if we would deploy it to Liferay right now.<br />
Therefore, it is a better idea to copy the content of the  _diffs folder of the production ready theme you want to start from into your own theme&#8217;s _diffs folder. To start from the default “Classic” Liferay theme, download the portal source(link) and copy the _diffs folder from the Classic theme at portal-web/docroot/html/themes to your own theme&#8217;s _diffs folder.<br />
Now your theme is production ready and ready for deployment.</p>
<h2>2. Deploying the theme to Liferay</h2>
<p>You probably already noticed the ant build script in the root of your project. If you run the default ant goal “deploy” of this script, the theme is published to your Liferay server.<br />
(If you are not doing this from within Eclipse, you need to properly set the values in the build.properties file in the ssdk&#8217;s theme folder.)</p>
<p>Let&#8217;s deploy and enable our theme on our Liferay server, which for now, looks exactly the same as the default “Classic” theme. If the theme deployed successfully, the Liferay logging should contain something like:</p>
<p><a href="http://www.integratingstuff.com/wp-content/uploads/2011/11/theme_deploy_logging.png"><img src="http://www.integratingstuff.com/wp-content/uploads/2011/11/theme_deploy_logging.png" alt="Liferay Theme being deployed - logging" title="Liferay Theme being deployed - logging" width="484" height="30" class="aligncenter size-full wp-image-506" /></a></p>
<div class="is_note">
Note: To set the theme on a page on Liferay and test it, go to Manage&gt;Page&gt;Look and Feel, and select our new theme there. Note that the theme description is lacking a thumbnail. If you want to add one, take a screenshot of a Liferay page with your theme applied, name it thumbnail.png, scale it down to 150 pixels wide and 120 pixels high and put in in the _diffs/images folder folder, as described in <a href="http://www.liferay.com/documentation/liferay-portal/6.0/development/-/ai/thumbnails">the Liferay documentation on theme thumbnails</a>.
</div>
<h2>3. Overriding parts of the parent theme</h2>
<p>The content of the_diffs folder we copied from the Classic theme consisted of 3 folders: css, images and js(javascript). In the _styled theme there is one additional folder, templates, of which the classic theme does not override anything. This is recommended practice: themes should try to avoid overriding template files since they are likely to change in between Liferay versions so changing them might make porting the theme to a new Liferay version more difficult.</p>
<h3>a. overriding css</h3>
<p>Now, to add a nice gradient background image and change the default text size and font, we have to modify our custom.css file.</p>
<p>We will change the</p>
<pre class="is_code">
body {
	background: #EEF0F2;
	font-size: 11px;
}
</pre>
<p>into:</p>
<pre class="is_code">
body {
        background-color: #4F555B;
	background-image: url("../images/layout/bg-grad.gif");
	background-repeat: repeat-x;
	color: #FFFFFF;
	font-family: Verdana,Helvetica,sans-serif;
	font-size: 11px;
}
</pre>
<p>and also create a layout_folder in our images folder and add the file <a href="http://www.integratingstuff.com/wp-content/uploads/2011/11/bg-grad.gif">bg-grad.gif</a> to it. </p>
<p>If we redeploy the theme and load our Liferay page, we see that the background has changed and text appears a bit different. Pretty easy, huh.</p>
<p><a href="http://www.integratingstuff.com/wp-content/uploads/2011/11/liferay_background_changed.png"><img src="http://www.integratingstuff.com/wp-content/uploads/2011/11/liferay_background_changed.png" alt="Liferay background changed with new theme" title="Liferay background changed with new theme" width="596" height="339" class="alignnone size-full wp-image-511" /></a></p>
<div class="is_note">
Note: I also replaced the Liferay logo with the Integrating Stuff logo by navigating to Manage&gt;Settings&gt;Logo and uploading the logo file there.
</div>
<h3>b. Overriding and adding images</h3>
<p>In the previous section, we already added a background image to the theme. If you want to override an image, you just need to add it to the images folder as well, but the image needs to have the same folder structure and file name as the one you want to override.<br />
For example, if you want to override some emoticons, you would create an emoticons folder(you need to look at the _styled theme files to know what you can override) and start putting names there of files that also exist in the parent theme(angry.gif, blank.gif,..).</p>
<h3>c. Overriding the custom javascript</h3>
<p>This can be done the same way images are overridden. However, the _styled theme only has one javascript file: main.js, which, with Liferay 6, only contains some logic that keeps the Liferay breadcrumbs always on screen.</p>
<h3>d. Overriding the template files</h3>
<p>As mentioned already, the Liferay documentation advises to avoid overriding the template files. That being said, almost all projects I know off involved creating themes that were modifying these templates.<br />
If one wants to add or remove parts of the page and the modifications go beyond styling dom elements, these templates are usually changed. The view technology these files use is <a href="http://en.wikipedia.org/wiki/Apache_Velocity">Velocity</a>.</p>
<p>The two most important ones are portal-normal.vm – which renders the whole page &#8211; and portlet.vm – which renders one portlet. The other ones respectively define additional velocity variables(init_custom.vm), render the navigation bar(navigation.vm) and render the popup pages(portal_pop_up.vm).</p>
<p>Let&#8217;s take a look at the portal-normal.vm Velocity template:</p>
<pre class="is_code">
&lt;!DOCTYPE html&gt;

#parse ($init)

&lt;html class="#language("lang.dir")" dir="#language("lang.dir")" lang="$w3c_language_id"&gt;

&lt;head&gt;
	&lt;title&gt;$the_title - $company_name&lt;/title&gt;

	$theme.include($top_head_include)
&lt;/head&gt;

&lt;body class="$css_class"&gt;

#if($is_signed_in)
	#dockbar()
#end

&lt;div id="wrapper"&gt;
	&lt;a href="#main-content" id="skip-to-content"&gt;#language("skip-to-content")&lt;/a&gt;

	&lt;header id="banner" role="banner"&gt;
		&lt;hgroup id="heading"&gt;
			&lt;h1 class="company-title"&gt;
				&lt;a class="logo" href="$company_url" title="#language("go-to") $company_name"&gt;
					&lt;span&gt;$company_name&lt;/span&gt;
				&lt;/a&gt;
			&lt;/h1&gt;

			&lt;h2 class="community-title"&gt;
				&lt;a href="$community_default_url" title="#language("go-to") $community_name"&gt;
					&lt;span&gt;$community_name&lt;/span&gt;
				&lt;/a&gt;
			&lt;/h2&gt;

			&lt;h3 class="page-title"&gt;
				&lt;span&gt;$the_title&lt;/span&gt;
			&lt;/h3&gt;
		&lt;/hgroup&gt;

		#if(!$is_signed_in)
			&lt;a href="$sign_in_url" id="sign-in" rel="nofollow"&gt;$sign_in_text&lt;/a&gt;
		#end

		#if ($update_available_url)
			&lt;div class="popup-alert-notice"&gt;
				&lt;a class="update-available" href="$update_available_url"&gt;#language("updates-are-available-for-liferay")&lt;/a&gt;
			&lt;/div&gt;
		#end

		#if ($has_navigation)
			#parse ("$full_templates_path/navigation.vm")
		#end
	&lt;/header&gt;

	&lt;div id="content"&gt;
		&lt;nav class="site-breadcrumbs" id="breadcrumbs"&gt;
			&lt;h1&gt;
				&lt;span&gt;#language("breadcrumbs")&lt;/span&gt;
			&lt;/h1&gt;

			#breadcrumbs()
		&lt;/nav&gt;

		#if ($selectable)
			$theme.include($content_include)
		#else
			$portletDisplay.recycle()

			$portletDisplay.setTitle($the_title)

			$theme.wrapPortlet("portlet.vm", $content_include)
		#end
	&lt;/div&gt;

	&lt;footer id="footer" role="contentinfo"&gt;
		&lt;p class="powered-by"&gt;
			#language("powered-by") &lt;a href="http://www.liferay.com" rel="external"&gt;Liferay&lt;/a&gt;
		&lt;/p&gt;
	&lt;/footer&gt;
&lt;/div&gt;

&lt;/body&gt;

$theme.include($bottom_include)

&lt;/html&gt;
</pre>
<p>If one would want (and is allowed to) remove the “Powered by Liferay” line, he can easily do this by just removing the line containing the text.</p>
<p>If the static htmldesign/prototypes are already made by web designers, it is easy for developers to port this created design to Liferay by copying the html to the portal-normal.vm file and inserting the Velocity directives where required.<br />
All these Velocity templates are clearly readable. When taking a look at the above file, it is obvious how to replace the whole Liferay header, for example.</p>
<h3>4. More advanced stuff</h3>
<p>There is more advanced stuff one can use to fine tune a theme and is not covered by this tutorial – such as <a href="http://www.liferay.com/documentation/liferay-portal/6.0/development/-/ai/settings">adding settings</a> or <a href="http://www.liferay.com/documentation/liferay-portal/6.0/development/-/ai/color-schemes">color schemes</a> to a theme. Although this tutorial gets you started from a practical point of view, it is probably a good idea to scan through <a href="http://www.liferay.com/documentation/liferay-portal/6.0/development/-/ai/creating-liferay-them-5">the Liferay documentation on themes</a> as well.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.integratingstuff.com/2011/11/30/styling-liferay-creating-a-liferay-theme/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Adding a webserver to an Android app</title>
		<link>http://www.integratingstuff.com/2011/10/24/adding-a-webserver-to-an-android-app/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=adding-a-webserver-to-an-android-app</link>
		<comments>http://www.integratingstuff.com/2011/10/24/adding-a-webserver-to-an-android-app/#comments</comments>
		<pubDate>Mon, 24 Oct 2011 04:10:34 +0000</pubDate>
		<dc:creator>Steffen Luypaert</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[http protocol]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.integratingstuff.com/?p=474</guid>
		<description><![CDATA[In this article, I give some example code on how to implement a webserver in an Android app, a functionality that can be handy if you want to give your app a web interface that can be accessed from a laptop or computer, which could be useful to enable quicker data entry amongst other things.
I also show some example code on how to parse a multipart message, in case you want the client to be able to sent form data/upload files to the webserver.]]></description>
			<content:encoded><![CDATA[<p>In this article, I give some example code on how to implement a webserver in an Android app, a functionality that can be handy if you want to give your app a web interface that can be accessed from a laptop or computer, which could be useful to enable quicker data entry amongst other things.<br />
I also show some example code on how to parse a multipart message, in case you want the client to be able to sent form data/upload files to the webserver.</p>
<h1>WebServer class</h1>
<p>Everything that we are going to use is already present in the Android API. We are not going to need any additional libs.</p>
<p>A pragmatic implementation looks like this:</p>
<pre class="is_code">
package com.integratingstuff.android.webserver;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

import org.apache.http.HttpException;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpRequestHandlerRegistry;
import org.apache.http.protocol.HttpService;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;

import android.content.Context;

public class WebServer {

	public static boolean RUNNING = false;
	public static int serverPort = 8080;

	private static final String ALL_PATTERN = "*";
	private static final String EXCEL_PATTERN = "/*.xls";
	private static final String HOME_PATTERN = "/home.html";

	private Context context = null;

	private BasicHttpProcessor httpproc = null;
	private BasicHttpContext httpContext = null;
	private HttpService httpService = null;
	private HttpRequestHandlerRegistry registry = null;

	public WebServer(Context context) {
		this.setContext(context);

		httpproc = new BasicHttpProcessor();
		httpContext = new BasicHttpContext();

		httpproc.addInterceptor(new ResponseDate());
		httpproc.addInterceptor(new ResponseServer());
		httpproc.addInterceptor(new ResponseContent());
		httpproc.addInterceptor(new ResponseConnControl());

		httpService = new HttpService(httpproc,
		    new DefaultConnectionReuseStrategy(), new DefaultHttpResponseFactory());

		registry = new HttpRequestHandlerRegistry();

		//registry.register(HOME_PATTERN, new HomeCommandHandler(context));

		httpService.setHandlerResolver(registry);
	}

	private ServerSocket serverSocket;

	public void runServer() {
		try {
			serverSocket = new ServerSocket(serverPort);

			serverSocket.setReuseAddress(true);

			while (RUNNING) {
				try {
					final Socket socket = serverSocket.accept();

					DefaultHttpServerConnection serverConnection = new DefaultHttpServerConnection();

					serverConnection.bind(socket, new BasicHttpParams());

					httpService.handleRequest(serverConnection, httpContext);

					serverConnection.shutdown();
				} catch (IOException e) {
					e.printStackTrace();
				} catch (HttpException e) {
					e.printStackTrace();
				}
			}

			serverSocket.close();
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		RUNNING = false;
	}

	public synchronized void startServer() {
		RUNNING = true;
		runServer();
	}

	public synchronized void stopServer() {
		RUNNING = false;
		if (serverSocket != null) {
			try {
				serverSocket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public void setContext(Context context) {
		this.context = context;
	}

	public Context getContext() {
		return context;
	}
}
</pre>
<p>In the WebServer contructor we are initializing an HttpService. We are using all the default implementations here. The interesting part is the construction of the HttpRequestHandlerRegistry. As we will see, we will add a custom requesthandler to this registry which shall build the response for a particular request.</p>
<p>In the runServer method we actually start the server by initiating a low level ServerSocket and make it accept connections. We then continiously bind the socket to our webserver implementation and tell it to handle the request. </p>
<p>The startServer and stopServer methods are the methods actually used to start and stop the server.</p>
<p>In Android apps, a server like this should be run as an Android <a href="http://developer.android.com/reference/android/app/Service.html">Service</a>. So, we are implementing the following WebServerService:</p>
<pre class="is_code">
package com.integratingstuff.android.webserver;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class WebServerService extends Service {

	private WebServer server = null;

	@Override
	public void onCreate() {
		Log.i("HTTPSERVICE", "Creating and starting httpService");
		super.onCreate();
		server = new WebServer(this);
		server.startServer();
	}

	@Override
	public void onDestroy() {
		Log.i("HTTPSERVICE", "Destroying httpService");
		server.stopServer();
		super.onDestroy();
	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}
}
</pre>
<p>Which we can start from the Android application like this:</p>
<pre style="is_code">
Intent webServerService = new Intent(context, WebServerService.class);
context.startService(webServerService);
</pre>
<h1>HttpRequestHandler</h1>
<p>Our WebServer is now running, but we did not register any requesthandlers yet. In our webserver, we commented the following line:</p>
<pre class="is_code">
registry.register(HOME_PATTERN, new HomeCommandHandler(context));
</pre>
<p>Let&#8217;s uncomment it and implement the HomeCommandHandler:</p>
<pre class="is_code">
package com.integratingstuff.android.webserver;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.entity.ContentProducer;
import org.apache.http.entity.EntityTemplate;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestHandler;

import android.content.Context;

public class HomeCommandHandler implements HttpRequestHandler {
	private Context context = null;

	public HomeCommandHandler(Context context) {
		this.context = context;
	}

	@Override
	public void handle(HttpRequest request, HttpResponse response,
	    HttpContext httpContext) throws HttpException, IOException {
		HttpEntity entity = new EntityTemplate(new ContentProducer() {
			public void writeTo(final OutputStream outstream) throws IOException {
				OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
				String resp = "&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h1&gt;Home&lt;h1&gt;&lt;p&gt;This is the homepage.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;";

				writer.write(resp);
				writer.flush();
			}
		});
		response.setHeader("Content-Type", "text/html");
		response.setEntity(entity);
	}

	public Context getContext() {
		return context;
	}
}
</pre>
<p>We are mapping the pattern home.html to this requesthandler. This handler will send some basic html back as its response. </p>
<p>If you now surf to http://&lt;ip-of-device&gt;:8080/home.html, you should see the homepage with the &#8220;This is the homepage&#8221; message.</p>
<p>So now, our webserver is up and running and responding to home.html requests. You can easily add more requesthandlers to the registry, that match specific patterns or wildcard * patterns.</p>
<h1>MultipartParser</h1>
<p>The above can be easily found elsewhere, so to give this article some added value, I will quickly indicate below how to parse form date/get the contents of files contained in the request.</p>
<h3>Code</h3>
<p>We will need to parse a multipart request manually. I am just going to give the code snippets and I am not going into the parsing intrinsics, but basically, the http protocol defines boundary delimiters for multipart messages. What we are doing with the following MultipartParser is read the entire sent form into a Multivalued map(a map of which the value is a list of a certain type of object, so every key is mapped to a list of 1 or more values). Note that the MultipartParser actually uses a CaseInsensitiveMultivaluedMap that wraps a regular MultivaluedMap, because keys like &#8220;name&#8221; and &#8220;NAME&#8221; map to the same formproperty.</p>
<h4>MultipartParser</h4>
<pre class="is_code">
package com.integratingstuff.android.webserver;

import java.io.IOException;
import java.io.InputStream;

public class MultipartParser {

	public final static String SEP = "\n";

	private InputStream is;
	private byte[] boundaryBA;
	static private byte[] boundaryDelimiterBA = "--".getBytes();

	private MultivaluedMap<String, String> partHeaders;
	private PartInputStream partIS;

	private static int BOUNDARY_TYPE_START = 0;
	private static int BOUNDARY_TYPE_END = 1;
	// private static int BOUNDARY_TYPE_INVALID = 2;

	private byte[] buff;
	// the next byte to return
	private int buffIdx = 0;
	// The number of bytes that were set on the buffer (red from is);
	private int buffSize = 0;
	// the position of the next boundary ("--boundary"), -1 if does not exist in
	// this buffer
	private int boundryIdx = -1;
	// The index of the byte that is suspected of been the boundary
	private int saveIdx = 0;

	// This is a temp array that is used to read stuff into it.
	private byte[] temp = new byte[1024];

	public MultipartParser(InputStream is, String boundary) {
		this.is = is;
		boundaryBA = ("--" + boundary).getBytes();
		// make sure to allocate a buffer that is at least double then the
		// boundary length
		int buffLength = Math.max(8192, boundaryBA.length * 2);
		buff = new byte[buffLength];

	}

	private void shiftBuff() {
		System.arraycopy(buff, buffIdx, buff, 0, buffSize - buffIdx);

		buffSize -= buffIdx;
		saveIdx -= buffIdx;
		if (saveIdx < 0)
			saveIdx = 0;
		/*
		 * throw new RuntimeException("This should never happend, we found a bug.");
		 */
		boundryIdx = Math.max(-1, boundryIdx - buffIdx);
		buffIdx = 0;

		// for debug purposes
		// Arrays.fill(buff, buffIdx, buff.length-1, (byte)0);

	}

	public boolean nextPart() throws IOException {
		// if this is the first next just get rid of the PREAMBLE
		if (partIS == null) {
			partIS = new PartInputStream();
		}
		// clear the part/preamble bytes that were not read
		digestPartStream();
		if (digestBoundary() == BOUNDARY_TYPE_END)
			return false;
		partIS.setState(PartInputStream.STATE_NOT_ACTIVE);
		partIS = new PartInputStream();
		partHeaders = parseHeaders();
		return partHeaders != null;
	}

	public InputStream getPartBodyStream() {
		return partIS;
	}

	public MultivaluedMap<String, String> getPartHeaders() {
		return partHeaders;
	}

	// read till end of stream (next boundary)
	private void digestPartStream() throws IOException {
		while (partIS.read(temp) != -1) {
		}
	}

	private boolean compareByte(byte[] a, int aOffset, byte[] b, int bOffset,
	    int length) {
		for (int i = 0; i < length; i++) {
			if (a[aOffset + i] != b[bOffset + i])
				return false;
		}
		return true;
	}

	private int digestBoundary() throws IOException {
		// it might be that there is a new line before the boundary
		digestNewLine();

		// promote pointers to the end of the boundary
		buffIdx += boundaryBA.length;
		saveIdx += boundaryBA.length; // DO NOT DELETE

		// check if this is an end boundary
		int unredBytes = verifyByteReadyForRead(2);
		if (unredBytes >= 2) {
			if (compareByte(buff, buffIdx, boundaryDelimiterBA, 0,
			    boundaryDelimiterBA.length))
				return BOUNDARY_TYPE_END;
		}
		// OK
		digestNewLine();
		boundryIdx = -1;
		findBounderyIfNeeded();
		return BOUNDARY_TYPE_START;
	}

	private void findBounderyIfNeeded() {
		if (boundryIdx == -1) {
			boundryIdx = indexOf(buff, saveIdx, buffSize, boundaryBA);
			if (boundryIdx != -1) {
				int nlSize = 0;
				if (boundryIdx > 1) {
					if (buff[boundryIdx - 2] == '\r' &#038;&#038; buff[boundryIdx - 1] == '\n')
						nlSize = 2;
					else
						nlSize = 1;
				}
				if (boundryIdx == 1) {
					nlSize = 1;
				}

				saveIdx = boundryIdx - nlSize;
			} else {
				// the boundary was not found, but we can promote the save till
				// boundary size + NL size
				saveIdx = Math.max(saveIdx, buffSize - (boundaryBA.length + 2));
			}
		}
	}

	private int verifyByteReadyForRead(int required) throws IOException {
		int unreadBytes = buffSize - buffIdx - 1;
		if (unreadBytes < required) {
			fetch(required - unreadBytes);
			unreadBytes = buffSize - buffIdx;
		}
		return unreadBytes;
	}

	private int fetch(int minmum) throws IOException {
		int res = 0;
		int max2featch = buff.length - buffSize;

		if (max2featch < minmum) {
			shiftBuff();
			max2featch = buff.length - buffSize;
		}

		while (res < minmum &#038;&#038; max2featch > 0) {
			max2featch = buff.length - buffSize;

			int read = is.read(buff, buffSize, max2featch);
			if (read == -1) {
				if (res == 0)
					return -1;
				else
					break;
			}
			res += read;
			buffSize += read;
		}
		findBounderyIfNeeded();
		return res;

	}

	private void digestNewLine() throws IOException {
		// make sure we have enough byte to read
		int unreadBytes = verifyByteReadyForRead(2);
		int size = 0;

		if (unreadBytes >= 2 &#038;&#038; buff[buffIdx] == '\r' &#038;&#038; buff[buffIdx + 1] == '\n')
			size = 2;
		else if (buff[buffIdx] == '\r')
			size = 1;
		else if (buff[buffIdx] == '\n')
			size = 1;
		buffIdx += size;
		if (saveIdx < buffIdx)
			saveIdx = buffIdx;
	}

	private int indexOf(byte[] ba, int start, int end, byte[] what) {
		for (int i = start; i < end - what.length + 1; i++) {
			// only if the first byte equals do the compare (to improve
			// performance)
			if (ba[i] == what[0])
				if (compareByte(ba, i, what, 0, what.length))
					return i;
		}
		return -1;
	}

	private MultivaluedMap<String, String> parseHeaders() throws IOException {

		MultivaluedMap<String, String> headers = new CaseInsensitiveMultivaluedMap<String>();

		String line;
		do {
			line = readLine();
			if (line == null || line.equals(""))
				break;
			int semIdx = line.indexOf(":");
			headers.add(line.substring(0, semIdx).trim(), line.substring(semIdx + 1)
			    .trim());

		} while (true);
		if (saveIdx < buffIdx)
			saveIdx = buffIdx;
		return headers;
	}

	private String readLine() throws IOException {

		int lineIdx = 0;
		int breakeSize = 0;
		while (lineIdx <= verifyByteReadyForRead(lineIdx)) {
			if (buff[buffIdx + lineIdx] == '\n') {
				breakeSize = 1;
				break;
			}
			if (buff[buffIdx + lineIdx] == '\r') {
				if ((verifyByteReadyForRead(lineIdx + 1) >= lineIdx + 1)
				    &#038;&#038; (buff[buffIdx + lineIdx + 1] == '\n')) {
					breakeSize = 2;
					break;
				} else {
					breakeSize = 1;
					break;
				}
			}
			lineIdx++;
		}

		// got to the end of input without NL
		if (lineIdx == 0) {
			buffIdx += breakeSize;
			return null;
		}

		String hdr = new String(buff, buffIdx, lineIdx);
		buffIdx += lineIdx + breakeSize;
		return hdr;
	}

	public class PartInputStream extends InputStream {
		// The state of the part Stream
		// 0 active
		// 1 not active (the Parser already moved to the next part.)
		private int state = 0;
		public final static int STATE_ACTIVE = 0;
		public final static int STATE_NOT_ACTIVE = 1;

		public void setState(int status) {
			this.state = status;

		}

		@Override
		public int read(byte[] b, int off, int len) throws IOException {
			if (state == STATE_NOT_ACTIVE) {
				throw new IOException(
				    "Stream allready closed: the PartInputStream is not accesable after moving to the next part");
			}
			int available = verifyNumOfByteToReadB4Boundary(len);
			if (available < 1) {
				return available
                        }
			int size2copy = Math.min(len, available);
			System.arraycopy(buff, buffIdx, b, off, size2copy);
			buffIdx += size2copy;
			return size2copy;
		}

		@Override
		public int read() throws IOException {
			if (state == STATE_NOT_ACTIVE) {
				throw new IOException(
				    "Stream allready closed: the PartInputStream is not accesable after moving to the next part");
			}
			int i = verifyNumOfByteToReadB4Boundary(1);
			if (i < 1)
				return -1;
			// make sure that the return value is 0 - 255
			int res = buff[buffIdx] &#038; 0xff;
			if (res < 0) {
				int t = 0;
				t++;
			}
			buffIdx++;
			return res;

		}

		private int verifyNumOfByteToReadB4Boundary(int minmum) throws IOException {
			int availabe = saveIdx - buffIdx;
			if (availabe >= minmum)
				return availabe;
			//
			if (saveIdx <= boundryIdx) {
				if (availabe == 0)
					return -1;
				return availabe;
			}
			int fetched = fetch(minmum - availabe);
			availabe = saveIdx - buffIdx;
			if (availabe == 0 &#038;&#038; fetched == -1)
				return -1;

			return availabe;

		}

		@Override
		public int available() {
			return saveIdx - buffIdx;
		}
	}
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.integratingstuff.com/2011/10/24/adding-a-webserver-to-an-android-app/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Continuous integration on Liferay: running your Selenium 2 tests on the Tomcat 6 bundle</title>
		<link>http://www.integratingstuff.com/2011/09/29/continuous-integration-on-liferay-running-your-selenium-2-tests-on-the-tomcat-6-bundle/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=continuous-integration-on-liferay-running-your-selenium-2-tests-on-the-tomcat-6-bundle</link>
		<comments>http://www.integratingstuff.com/2011/09/29/continuous-integration-on-liferay-running-your-selenium-2-tests-on-the-tomcat-6-bundle/#comments</comments>
		<pubDate>Thu, 29 Sep 2011 18:45:36 +0000</pubDate>
		<dc:creator>Steffen Luypaert</dc:creator>
				<category><![CDATA[Liferay]]></category>
		<category><![CDATA[Selenium]]></category>
		<category><![CDATA[Cargo]]></category>
		<category><![CDATA[Continuous Integration]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[Testing]]></category>
		<category><![CDATA[Tomcat]]></category>

		<guid isPermaLink="false">http://www.integratingstuff.com/?p=446</guid>
		<description><![CDATA[In this article we are going to write a Selenium 2 test for the <a href="http://www.liferay.com">Liferay</a> portlet we developed in <a href="http://www.integratingstuff.com/2011/05/29/getting-started-with-portals-writing-a-portlet-with-spring-portlet-mvc-and-deploying-it-to-a-portal/">Getting started with portals</a>. Then we are going to add a profile to our <a href="http://maven.apache.org/">Maven</a> pom that, when activated, starts the Liferay server and runs the selenium 2 test after which the Liferay server is stopped again.
This is ideal for Liferay integration testing, especially when the process needs to be fully automated(e.g. no manual stop/start of server), such as by a continuous integration server like Hudson or <a href="http://jenkins-ci.org/">Jenkins</a>.]]></description>
			<content:encoded><![CDATA[<p>In this article we are going to write a Selenium 2 test for the <a href="http://www.liferay.com">Liferay</a> portlet we developed in <a href="http://www.integratingstuff.com/2011/05/29/getting-started-with-portals-writing-a-portlet-with-spring-portlet-mvc-and-deploying-it-to-a-portal/">Getting started with portals</a>. Then we are going to add a profile to our <a href="http://maven.apache.org/">Maven</a> pom that, when activated, starts the Liferay server and runs the selenium 2 test after which the Liferay server is stopped again.<br />
This is ideal for Liferay integration testing, especially when the process needs to be fully automated(e.g. no manual stop/start of server), such as by a continuous integration server like Hudson or <a href="http://jenkins-ci.org/">Jenkins</a>.</p>
<h2>The Selenium 2 test</h2>
<p>We implement a Selenium 2 test similar to the one we introduced in <a href="http://www.integratingstuff.com/2011/08/31/converting-selenium-1-to-selenium-2-tests/">Converting Selenium 1 to Selenium 2 tests</a>. We are using <a href="http://download.oracle.com/javase/7/docs/api/java/lang/System.html#getProperty(java.lang.String)">System.getProperty</a> calls now. A System.getProperty call fetches a certain system property. These system properties will be set by Maven first, as we will see later.</p>
<pre class="is_code">
import java.io.File;

import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxBinary;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

public class PortalPageTest {

  private FirefoxDriver createFirefoxDriver() {
    File firefoxBin = new File(System.getProperty("selenium.pathToFirefoxBinary"));
    FirefoxBinary firefoxBinary = new FirefoxBinary(firefoxBin);
    FirefoxDriver driver = new FirefoxDriver(firefoxBinary, null);
    return driver;
  }

  @Test
  public void testPortalPage() {
    final FirefoxDriver firefoxDriver = createFirefoxDriver();

    firefoxDriver.get(System.getProperty("selenium.baseURL"));

    final PortalPage portalPage = new PortalPage();
    WebDriverWait wait = new WebDriverWait(firefoxDriver, 20 * 1000, 200);
    wait.until(new ExpectedCondition() {
      @Override
      public Boolean apply(WebDriver driver) {
        PageFactory.initElements(firefoxDriver, portalPage);
        return portalPage.testPortlet.getText().equals("This is our test portlet.");
      }
    });
  }
}
</pre>
<p>And the related Page object:</p>
<pre class="is_code">
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class PortalPage {

  @FindBy(id = "testPortlet")
  public WebElement testPortlet;

}
</pre>
<h2>The Maven profile</h2>
<p>In summary, our Maven profile will:</p>
<ol>
<li>Clean the Liferay server directories during the clean phase.</li>
<li>Start the Liferay server, deploy the application and wait 5 minutes for the application to finish deploying during the pre-integration-test phase.</li>
<li>Execute the Selenium 2 tests. It will not fail if any of the tests fails but instead, it will write the results away.</li>
<li>Stop the Liferay server during the post-integration-test phase.</li>
</ol>
<h3>Profile breakup</h3>
<p>We build up the profile part by part.</p>
<h4>1. Activation</h4>
<pre class="is_code">
&lt;activation&gt;
    &lt;property&gt;
      &lt;name&gt;liferay.home.dir&lt;/name&gt;
    &lt;/property&gt;
&lt;/activation&gt;
</pre>
<p>The profile is getting activated when we supply a liferay.home.dir. However, this is not the only property we are going to need for a successful run. We need to give all the necessary info about the browser we are going to run the Selenium tests with and the server we are going to run the tests on. So, next to the liferay.home.dir, we will also need to supply the selenium.firefox.binary and the liferay.port properties.<br />
Hence, to do an install with the profile enabled, we will need to run Maven with a goal like this:</p>
<pre class="is_code">
clean install –Dliferay.home.dir=/development/Liferay
–Dselenium.firefox.binary=/opt/firefox/firefox.sh –Dliferay.port=8080
</pre>
<h4>2. Cargo plugin</h4>
<pre class="is_code">
&lt;plugin&gt;
  &lt;groupId&gt;org.codehaus.cargo&lt;/groupId&gt;
  &lt;artifactId&gt;cargo-maven2-plugin&lt;/artifactId&gt;
  &lt;version&gt;1.0.6&lt;/version&gt;
  &lt;executions&gt;
    &lt;execution&gt;
      &lt;id&gt;start-liferay&lt;/id&gt;
      &lt;phase&gt;pre-integration-test&lt;/phase&gt;
      &lt;goals&gt;
        &lt;goal&gt;start&lt;/goal&gt;
      &lt;/goals&gt;
    &lt;/execution&gt;
    &lt;execution&gt;
      &lt;id&gt;stop-liferay&lt;/id&gt;
      &lt;phase&gt;post-integration-test&lt;/phase&gt;
      &lt;goals&gt;
        &lt;goal&gt;stop&lt;/goal&gt;
      &lt;/goals&gt;
    &lt;/execution&gt;
  &lt;/executions&gt;
  &lt;configuration&gt;
    &lt;skip&gt;false&lt;/skip&gt;
    &lt;wait&gt;false&lt;/wait&gt;
    &lt;container&gt;
      &lt;containerId&gt;tomcat6x&lt;/containerId&gt;
      &lt;home&gt;${liferay.home.dir}/tomcat-6.0.23&lt;/home&gt;
      &lt;timeout&gt;30000&lt;/timeout&gt;
      &lt;type&gt;installed&lt;/type&gt;
      &lt;systemProperties&gt;
        &lt;file.encoding&gt;UTF8&lt;/file.encoding&gt;
        &lt;external-properties&gt;${liferay.home.dir}/portal-ext.properties
        &lt;/external-properties&gt;
      &lt;/systemProperties&gt;
    &lt;/container&gt;
    &lt;configuration&gt;
      &lt;home&gt;${liferay.home.dir}/tomcat-6.0.23&lt;/home&gt;
      &lt;type&gt;existing&lt;/type&gt;
      &lt;properties&gt;
        &lt;cargo.jvmargs&gt;-Xmx1024m -XX:MaxPermSize=512m&lt;/cargo.jvmargs&gt;
        &lt;cargo.servlet.port&gt;${liferay.port}&lt;/cargo.servlet.port&gt;
        &lt;cargo.logging&gt;high&lt;/cargo.logging&gt;
      &lt;/properties&gt;
    &lt;/configuration&gt;
  &lt;/configuration&gt;
&lt;/plugin&gt;
</pre>
<p>The Cargo plugin is going to start and stop Liferay in a talled application server container. The server is started before integration testing begins and stopped after integration testing. In the configuration part of the plugin, we configure our server: where it is located, the vmargs we want to start it with, etc..<br />
The &lt;skip&gt; part of the configuration means we want Cargo to run and start the server. If set to true, the execution of this plugin would be skipped and no container would be started.<br />
The &lt;wait&gt; part of the configuration determines whether we want to wait after the Cargo container is started or not. If set to true, we would need to manually intervene(as in pressing some buttons) to continue the Maven process. For automated integration testing, this obviously needs to be set to false.<br />
For more information about configuring the cargo plugin, visit <a href="http://cargo.codehaus.org/Maven2+Plugin+Reference+Guide">the Cargo maven plugin reference</a>.<br />
One thing to keep in mind is that the cargo.servlet.port should be the port Liferay runs on(8080 by default). If this port is not configured correctly, Cargo will timeout because it cannot determine whether the server started or not. It is the port Cargo listens on, not the port the server starts on. The server starts on the default port listed in its own configuration files.</p>
<h4>3. Liferay plugin</h4>
<pre class="is_code">
&lt;plugin&gt;
  &lt;groupId&gt;com.liferay.maven.plugins&lt;/groupId&gt;
  &lt;artifactId&gt;liferay-maven-plugin&lt;/artifactId&gt;
  &lt;version&gt;6.0.5&lt;/version&gt;
  &lt;executions&gt;
    &lt;execution&gt;
      &lt;id&gt;liferay-deploy&lt;/id&gt;
      &lt;phase&gt;pre-integration-test&lt;/phase&gt;
      &lt;goals&gt;
        &lt;goal&gt;deploy&lt;/goal&gt;
      &lt;/goals&gt;
    &lt;/execution&gt;
  &lt;/executions&gt;
  &lt;configuration&gt;
    &lt;autoDeployDir&gt;${liferay.home.dir}/deploy&lt;/autoDeployDir&gt;
  &lt;/configuration&gt;
&lt;/plugin&gt;
</pre>
<p>The above plugin deploys our war(the product of the pom) to the Liferay server. It also executes before integration testing begins. Note that in the default case, Maven plugins execute in the order in which they are defined. So in this case, the Liferay server is started first, after which the application(the portlets) is deployed to the started server.<br />
Note that this plugin deploys the application, but it does NOT wait for the app to be deployed. Maven execution continues right away, so we will need to build something in to wait for the app to be deployed, before we actually start running the Selenium 2 tests.</p>
<h4>4. Antrun plugin</h4>
<pre class="is_code">
&lt;plugin&gt;
  &lt;artifactId&gt;maven-antrun-plugin&lt;/artifactId&gt;
  &lt;executions&gt;
    &lt;execution&gt;
      &lt;id&gt;wait-for-liferay&lt;/id&gt;
      &lt;phase&gt;pre-integration-test&lt;/phase&gt;
      &lt;configuration&gt;
        &lt;tasks&gt;
          &lt;echo&gt;Wait 300 seconds for Liferay..&lt;/echo&gt;
          &lt;waitfor maxwait="300" maxwaitunit="second"&gt;
            &lt;available file="errors.log" /&gt;
          &lt;/waitfor&gt;
        &lt;/tasks&gt;
      &lt;/configuration&gt;
      &lt;goals&gt;
        &lt;goal&gt;run&lt;/goal&gt;
      &lt;/goals&gt;
    &lt;/execution&gt;
    &lt;execution&gt;
      &lt;id&gt;clean-liferay&lt;/id&gt;
      &lt;phase&gt;clean&lt;/phase&gt;
      &lt;configuration&gt;
        &lt;tasks&gt;
          &lt;echo&gt;Cleaning Liferay..&lt;/echo&gt;
          &lt;delete
            dir="${liferay.home.dir}/tomcat-6.0.23/webapps/${project.build.finalName}"
            quiet="true" /&gt;
          &lt;delete dir="${liferay.home.dir}/tomcat-6.0.23/work"
            quiet="true" /&gt;
          &lt;delete dir="${liferay.home.dir}/tomcat-6.0.23/temp"
            quiet="true" /&gt;
        &lt;/tasks&gt;
      &lt;/configuration&gt;
      &lt;goals&gt;
        &lt;goal&gt;run&lt;/goal&gt;
      &lt;/goals&gt;
    &lt;/execution&gt;
  &lt;/executions&gt;
&lt;/plugin&gt;
</pre>
<p>The <a href="http://maven.apache.org/plugins/maven-antrun-plugin/">antrun plugin</a> enables all <a href="http://ant.apache.org/">Ant</a> functionality within a Maven context.<br />
This the dirty part of our profile. If there is something to improve, it is this part.<br />
We are essentially doing two things here.<br />
Before integration testing begins, we are waiting 300 seconds. This gives the application enough time to deploy, so it is ready once the selenium tests start running. Note that you could use something more intelligent here than plain waiting, such as polling the server to check whether a certain page is loading or even building a Liferay hook that when deployed, can be used to ask Liferay through a webservice whether it has finished deploying or not. However, that would require building something custom and is outside of the scope of this article.<br />
The second ant execution configured for the plugin, cleans the Liferay server in the clean phase, so we start with a clean Liferay server every run. We had to add this because we were having issues with the new deploy conflicting with certain parts of an old deploy from time to time. Among other things, this makes sure the old version is not deployed first when the server starts. Same as with the other execution, one could come up with more sophisticated ways to do this, but it should be enough to illustrate the idea.</p>
<h4>5. Surefire plugin</h4>
<pre class="is_code">
&lt;plugin&gt;
  &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
  &lt;artifactId&gt;maven-surefire-plugin&lt;/artifactId&gt;
  &lt;version&gt;2.7.2&lt;/version&gt;
  &lt;configuration&gt;
    &lt;skip&gt;true&lt;/skip&gt;
  &lt;/configuration&gt;
&lt;/plugin&gt;
</pre>
<p>The <a href="http://maven.apache.org/plugins/maven-surefire-plugin/">surefire plugin</a> is the default plugin to run unit tests with. Because we want to use the failsafe plugin in the next section, the execution of this plugin should be skipped. Therefore, we are setting &lt;skip&gt; to true.</p>
<h4>6. Failsafe plugin</h4>
<pre class="is_code">
&lt;plugin&gt;
  &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
  &lt;artifactId&gt;maven-failsafe-plugin&lt;/artifactId&gt;
  &lt;version&gt;2.9&lt;/version&gt;
  &lt;executions&gt;
    &lt;execution&gt;
      &lt;id&gt;selenium2-firefox-test&lt;/id&gt;
      &lt;phase&gt;integration-test&lt;/phase&gt;
      &lt;goals&gt;
        &lt;goal&gt;integration-test&lt;/goal&gt;
      &lt;/goals&gt;
      &lt;configuration&gt;
        &lt;includes&gt;
          &lt;include&gt;**/SeleniumIntegrationTestSuite.java
                &lt;/include&gt;
        &lt;/includes&gt;
        &lt;systemPropertyVariables&gt;
          &lt;selenium.baseURL&gt;http://localhost:${liferay.port}/web/guest/testing
          &lt;/selenium.baseURL&gt;
          &lt;selenium.pathToFirefoxBinary&gt;${selenium.firefox.binary}
          &lt;/selenium.pathToFirefoxBinary&gt;
        &lt;/systemPropertyVariables&gt;
        &lt;reportsDirectory&gt;${project.build.directory}/failsafe-reports/firefox
        &lt;/reportsDirectory&gt;
      &lt;/configuration&gt;
    &lt;/execution&gt;
  &lt;/executions&gt;
&lt;/plugin&gt;
</pre>
<p>With the <a href="http://maven.apache.org/plugins/maven-failsafe-plugin/">failsafe plugin</a>, we are going to run our Selenium 2 test. The main difference with the surefire plugin is that this plugin will make sure the Maven execution keeps executing is some integration tests fail.<br />
There is also some additional configuration required.<br />
Basically, we make a Suite class to which we add our test:</p>
<pre class="is_code">
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({ PortalPageTest.class })
public class SeleniumIntegrationTestSuite{
}
</pre>
<p>All tests defined in this suite will be run using the supplied firefox executable. Note that the path to the firefox bin is not part of the failsafe plugin, but is a system property that we are setting. This system property is then fetched within the running Selenium 2 test. Same for the Selenium base url. Note that in order to run the test (suite) successfully, you will need to add a page &#8220;testing&#8221; to your Liferay portal and make sure the portlet is deployed on that page first.<br />
Finally, the outcome of all tests will be written to htmlfiles in the supplied reportsDirectory.</p>
<h3>The complete profile</h3>
<p>To end, we are showing the entire Maven profile here for reference:</p>
<pre class="is_code">
&lt;profile&gt;
  &lt;id&gt;liferay&lt;/id&gt;
  &lt;activation&gt;
    &lt;property&gt;
      &lt;name&gt;liferay.home.dir&lt;/name&gt;
    &lt;/property&gt;
  &lt;/activation&gt;
  &lt;build&gt;
    &lt;plugins&gt;
      &lt;plugin&gt;
        &lt;groupId&gt;org.codehaus.cargo&lt;/groupId&gt;
        &lt;artifactId&gt;cargo-maven2-plugin&lt;/artifactId&gt;
        &lt;version&gt;1.0.6&lt;/version&gt;
        &lt;executions&gt;
          &lt;execution&gt;
            &lt;id&gt;start-liferay&lt;/id&gt;
            &lt;phase&gt;pre-integration-test&lt;/phase&gt;
            &lt;goals&gt;
              &lt;goal&gt;start&lt;/goal&gt;
            &lt;/goals&gt;
          &lt;/execution&gt;
          &lt;execution&gt;
            &lt;id&gt;stop-liferay&lt;/id&gt;
            &lt;phase&gt;post-integration-test&lt;/phase&gt;
            &lt;goals&gt;
              &lt;goal&gt;stop&lt;/goal&gt;
            &lt;/goals&gt;
          &lt;/execution&gt;
        &lt;/executions&gt;
        &lt;configuration&gt;
          &lt;skip&gt;false&lt;/skip&gt;
          &lt;wait&gt;false&lt;/wait&gt;
          &lt;container&gt;
            &lt;containerId&gt;tomcat6x&lt;/containerId&gt;
            &lt;home&gt;${liferay.home.dir}/tomcat-6.0.23
            &lt;/home&gt;
            &lt;timeout&gt;30000&lt;/timeout&gt;
            &lt;type&gt;installed&lt;/type&gt;
            &lt;systemProperties&gt;
              &lt;file.encoding&gt;UTF8&lt;/file.encoding&gt;
              &lt;external-properties&gt;${liferay.home.dir}/portal-ext.properties
              &lt;/external-properties&gt;
            &lt;/systemProperties&gt;
          &lt;/container&gt;
          &lt;configuration&gt;
            &lt;home&gt;${liferay.home.dir}/tomcat-6.0.23&lt;/home&gt;
            &lt;type&gt;existing&lt;/type&gt;
            &lt;properties&gt;
              &lt;cargo.jvmargs&gt;-Xmx1024m -XX:MaxPermSize=512m
              &lt;/cargo.jvmargs&gt;
              &lt;cargo.servlet.port&gt;${liferay.port}&lt;/cargo.servlet.port&gt;
              &lt;cargo.logging&gt;high&lt;/cargo.logging&gt;
            &lt;/properties&gt;
          &lt;/configuration&gt;
        &lt;/configuration&gt;
      &lt;/plugin&gt;

      &lt;plugin&gt;
        &lt;groupId&gt;com.liferay.maven.plugins&lt;/groupId&gt;
        &lt;artifactId&gt;liferay-maven-plugin&lt;/artifactId&gt;
        &lt;version&gt;6.0.5&lt;/version&gt;
        &lt;executions&gt;
          &lt;execution&gt;
            &lt;id&gt;liferay-deploy&lt;/id&gt;
            &lt;phase&gt;pre-integration-test&lt;/phase&gt;
            &lt;goals&gt;
              &lt;goal&gt;deploy&lt;/goal&gt;
            &lt;/goals&gt;
          &lt;/execution&gt;
        &lt;/executions&gt;
        &lt;configuration&gt;
          &lt;autoDeployDir&gt;${liferay.home.dir}/deploy&lt;/autoDeployDir&gt;
        &lt;/configuration&gt;
      &lt;/plugin&gt;

      &lt;plugin&gt;
        &lt;artifactId&gt;maven-antrun-plugin&lt;/artifactId&gt;
        &lt;executions&gt;
          &lt;execution&gt;
            &lt;id&gt;wait-for-liferay&lt;/id&gt;
            &lt;phase&gt;pre-integration-test&lt;/phase&gt;
            &lt;configuration&gt;
              &lt;tasks&gt;
                &lt;echo&gt;Wait 300 seconds for Liferay..&lt;/echo&gt;
                &lt;waitfor maxwait="300" maxwaitunit="second"&gt;
                  &lt;available file="errors.log" /&gt;
                &lt;/waitfor&gt;
              &lt;/tasks&gt;
            &lt;/configuration&gt;
            &lt;goals&gt;
              &lt;goal&gt;run&lt;/goal&gt;
            &lt;/goals&gt;
          &lt;/execution&gt;
          &lt;execution&gt;
            &lt;id&gt;clean-liferay&lt;/id&gt;
            &lt;phase&gt;clean&lt;/phase&gt;
            &lt;configuration&gt;
              &lt;tasks&gt;
                &lt;echo&gt;Cleaning Liferay..&lt;/echo&gt;
                &lt;delete
                  dir="${liferay.home.dir}/tomcat-6.0.23/webapps/${project.build.finalName}"
                  quiet="true" /&gt;
                &lt;delete dir="${liferay.home.dir}/tomcat-6.0.23/work"
                  quiet="true" /&gt;
                &lt;delete dir="${liferay.home.dir}/tomcat-6.0.23/temp"
                  quiet="true" /&gt;
              &lt;/tasks&gt;
            &lt;/configuration&gt;
            &lt;goals&gt;
              &lt;goal&gt;run&lt;/goal&gt;
            &lt;/goals&gt;
          &lt;/execution&gt;
        &lt;/executions&gt;
      &lt;/plugin&gt;

      &lt;plugin&gt;
        &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
        &lt;artifactId&gt;maven-failsafe-plugin&lt;/artifactId&gt;
        &lt;version&gt;2.9&lt;/version&gt;
        &lt;executions&gt;
          &lt;execution&gt;
            &lt;id&gt;selenium2-firefox-test&lt;/id&gt;
            &lt;phase&gt;integration-test&lt;/phase&gt;
            &lt;goals&gt;
              &lt;goal&gt;integration-test&lt;/goal&gt;
            &lt;/goals&gt;
            &lt;configuration&gt;
              &lt;includes&gt;
                &lt;include&gt;**/SeleniumIntegrationTestSuite.java
                &lt;/include&gt;
              &lt;/includes&gt;
              &lt;systemPropertyVariables&gt;
                &lt;selenium.baseURL&gt;http://localhost:${liferay.port}/web/guest/testing
                &lt;/selenium.baseURL&gt;
                &lt;selenium.pathToFirefoxBinary&gt;${selenium.firefox.binary}
                &lt;/selenium.pathToFirefoxBinary&gt;
              &lt;/systemPropertyVariables&gt;
              &lt;reportsDirectory&gt;${project.build.directory}/failsafe-reports/firefox
              &lt;/reportsDirectory&gt;
            &lt;/configuration&gt;
          &lt;/execution&gt;
        &lt;/executions&gt;
      &lt;/plugin&gt;

      &lt;plugin&gt;
        &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
        &lt;artifactId&gt;maven-surefire-plugin&lt;/artifactId&gt;
        &lt;version&gt;2.7.2&lt;/version&gt;
        &lt;configuration&gt;
          &lt;skip&gt;true&lt;/skip&gt;
        &lt;/configuration&gt;
      &lt;/plugin&gt;

    &lt;/plugins&gt;
  &lt;/build&gt;
&lt;/profile&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.integratingstuff.com/2011/09/29/continuous-integration-on-liferay-running-your-selenium-2-tests-on-the-tomcat-6-bundle/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Converting Selenium 1 to Selenium 2 Tests</title>
		<link>http://www.integratingstuff.com/2011/08/31/converting-selenium-1-to-selenium-2-tests/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=converting-selenium-1-to-selenium-2-tests</link>
		<comments>http://www.integratingstuff.com/2011/08/31/converting-selenium-1-to-selenium-2-tests/#comments</comments>
		<pubDate>Wed, 31 Aug 2011 19:42:41 +0000</pubDate>
		<dc:creator>Steffen Luypaert</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Selenium]]></category>
		<category><![CDATA[Web frameworks]]></category>
		<category><![CDATA[Testing]]></category>

		<guid isPermaLink="false">http://www.integratingstuff.com/?p=432</guid>
		<description><![CDATA[This article is about Selenium and the difference between Selenium IDE and Selenium WebDriver. It also discusses how to convert Selenium 1 tests to Selenium 2 tests and, in the last section, covers the current state of Selenium 2 and mentions some issues one can encounter when using the WebDriver API.]]></description>
			<content:encoded><![CDATA[<p>This article is about Selenium and the difference between Selenium IDE and Selenium WebDriver. It also discusses how to convert Selenium 1 tests to Selenium 2 tests and, in the last section, covers the current state of Selenium 2 and mentions some issues one can encounter when using the WebDriver API.</p>
<h1>What is Selenium?</h1>
<p>As <a href="http://seleniumhq.org/">the Selenium homepage</a> states, Selenium automates browsers. With Selenium, you can script your browser. Usually, it is used for automated testing of web applications, but sometimes it is used to automate certain repetitive web-based tasks as well. </p>
<h1>Selenium tests</h1>
<p>When using Selenium tests, there is one big choice one has to make: whether to use <a href="http://seleniumhq.org/projects/ide/">Selenium IDE</a> or <a href="http://seleniumhq.org/projects/webdriver/">Selenium WebDriver</a>.</p>
<h3>Selenium IDE tests</h3>
<p>Selenium IDE is a Firefox add-on that will do simple record-and-playback of interactions with the browser.<br />
You can download it from the Selenium website and install it as a plugin. Once it is installed, open Firefox, go to Tools&gt;Selenium IDE. We are going to record a simple test that goes to dzone.com and clicks on the “New Links” link.<br />
To record, just press the red button in the IDE. Selenium IDE starts recording now. Everytime you click or type something in Firefox, the action will be recorded in the script, and in the end, Selenium IDE will be able to reproduce everything you did when interacting with the browser.</p>
<p><a href="http://www.integratingstuff.com/wp-content/uploads/2011/08/selenium_ide.png"><img src="http://www.integratingstuff.com/wp-content/uploads/2011/08/selenium_ide.png" alt="Selenium IDE" title="Selenium IDE" width="548" height="541" class="alignnone size-full wp-image-440" /></a></p>
<p>In our case, the resulting Selenium 1 tests looks like this(click on Source in Selenium IDE):</p>
<pre class="is_code">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"&gt;
&lt;head profile="http://selenium-ide.openqa.org/profiles/test-case"&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
&lt;link rel="selenium.base" href="http://www.dzone.com/" /&gt;
&lt;title&gt;New Test&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;table cellpadding="1" cellspacing="1" border="1"&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;td rowspan="1" colspan="3"&gt;New Test&lt;/td&gt;&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
	&lt;td&gt;open&lt;/td&gt;
	&lt;td&gt;/links/index.html&lt;/td&gt;
	&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
	&lt;td&gt;clickAndWait&lt;/td&gt;
	&lt;td&gt;link=New links&lt;/td&gt;
	&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>To run the test, you can just click the green arrow. If you do, Selenium IDE goes to the dzone.com website and clicks the “New Links” link.</p>
<h3>Selenium WebDriver tests</h3>
<p><a href="http://seleniumhq.org/docs/03_webdriver.html">Selenium WebDriver</a>  is a collection of language specific bindings(Java, Python,..) to drive a browser. Unlike Selenium IDE, Selenium WebDriver uses native calls to the browser.<br />
It was introduced with Selenium 2.0, which is a new and extended version of the Selenium 1.0 API. Usually, when talking about Selenium 2.0, people mean Selenium making use of the WebDriver API.<br />
Instead of recording tests with a plugin from your browser, Selenium WebDriver expects you to write code . If you want to code along in Java, you will have to fire up your Java  IDE – I am going to use Eclipse myself – and make sure you import the Selenium library jar and the driver jars into the project. If you are using Maven, the dependencies would look like:</p>
<pre class="is_code">
&lt;dependency&gt;
        &lt;groupId&gt;org.seleniumhq.selenium&lt;/groupId&gt;
	&lt;artifactId&gt;selenium-htmlunit-driver&lt;/artifactId&gt;
	&lt;version&gt;${selenium.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
        &lt;groupId&gt;org.seleniumhq.selenium&lt;/groupId&gt;
	&lt;artifactId&gt;selenium-chrome-driver&lt;/artifactId&gt;
	&lt;version&gt;${selenium.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
	&lt;groupId&gt;org.seleniumhq.selenium&lt;/groupId&gt;
	&lt;artifactId&gt;selenium-firefox-driver&lt;/artifactId&gt;
	&lt;version&gt;${selenium.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
        &lt;groupId&gt;org.seleniumhq.selenium&lt;/groupId&gt;
	&lt;artifactId&gt;selenium-ie-driver&lt;/artifactId&gt;
	&lt;version&gt;${selenium.version}&lt;/version&gt;
lt;/dependency&gt;
&lt;dependency&gt;
	&lt;groupId&gt;org.seleniumhq.selenium&lt;/groupId&gt;
	&lt;artifactId&gt;selenium-support&lt;/artifactId&gt;
	&lt;version&gt;${selenium.version}&lt;/version&gt;
&lt;/dependency&gt;
</pre>
<p>Note: Selenium libraries for other programming languages such as Python are available as well. The approach for those is just the same.</p>
<p>Our Selenium 2 test looks like this:</p>
<pre class="is_code">
public class SeleniumTestCase {
	@Test
	public void testFramework() {
		File firefoxBin =
                     new File("C:\\Users\\Public\\Mozilla Firefox 3.6\\firefox.exe");
		FirefoxBinary firefoxBinary = new FirefoxBinary(firefoxBin);
		final FirefoxDriver firefoxDriver =
                     new FirefoxDriver(firefoxBinary, null);

		firefoxDriver.get("http://www.dzone.com");

		final DzonePage dzonePage = new DzonePage();

		WebDriverWait wait = new WebDriverWait(firefoxDriver, 30*1000, 200);
		wait.until(new ExpectedCondition<Boolean>() {
		      @Override
		      public Boolean apply(WebDriver driver) {
		    	  PageFactory.initElements(firefoxDriver, dzonePage);
		    	  return dzonePage.newLinksLink.isDisplayed();
		      }
		});

		dzonePage.newLinksLink.click();
	}
}
</pre>
<p>First, the driver is instantiated. For Firefox, it is necessary to supply the driver with the location of the Firefox binary. For some drivers, such as the InternetExplorerDriver, this is not necessary.<br />
Then we instruct the driver to send a http get request to www.dzone.com.  After that, we wait for the page to load. We do this by instantiating <a href="http://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/support/ui/WebDriverWait.html">WebDriverWait</a>, which wraps our driver into a construct which allows it to wait for a condition. In this case, we want to wait for the “new links” link to be displayed, so we can click it.<br />
Note that in Selenium 2 there is a Page class concept. This page is an abstract representation of the opened  webpage and looks like this:</p>
<pre class="is_code">
public class DzonePage{
	@FindBy(linkText = "New links")
	public WebElement newLinksLink;

	@FindBy(tagName = "body")
	public WebElement body;
}
</pre>
<p>From within Eclipse, this test can be run like “Run as&gt;jUnit Test”.</p>
<h1>Converting from Selenium 1 to Selenium 2</h1>
<p>If you have a bunch of selenium 1 tests you have to convert, you can consider automating the conversion. The selenium1 tests can easily be parsed as xml files and you can come up with code snippets that map one to one on the selenium1 commands.<br />
However, writing a framework like that is beyond the scope of this article. I am just going to discuss some issues I encountered when doing such conversion.</p>
<h3>Coming up with Selenium WebDriver equivalents of the Selenium IDE commands.</h3>
<p>Although many mappings are rather straightforward, and there is also <a href="http://seleniumhq.org/docs/appendix_migrating_from_rc_to_webdriver.html">a guide for converting Selenium 1 to Selenium 2 tests</a>, some are a bit harder to come up with.<br />
For example, it is often necessary to wait for a certain element to load after doing a click. We already saw that in our example. Generic code that waits on the element that needs to be loaded, could be implemented like this:</p>
<pre class="is_code">
protected final void waitForExistsAndVisible(final WebElement element) {
     seleniumContext.getWebDriverWait().until(new ExpectedCondition<Boolean>() {
	@Override
	public Boolean apply(WebDriver driver) {
		return element.isDisplayed();
	}
     });
}
</pre>
<p>From time to time, there are some catches too. For example, to wait for an element not to be present, one could expect <a href="http://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/WebDriver.html#findElement(org.openqa.selenium.By)">driver.findElement</a> to return null if the element is not present. However, this method never returns null. If the element is not present, a NoSuchElementException is thrown instead. Hence, the method needs to look like:</p>
<pre class="is_code">
protected final void waitForElementNotPresent(final String htmlId) {
     seleniumContext.getWebDriverWait().until(new ExpectedCondition<Boolean>() {
	@Override
	public Boolean apply(WebDriver driver) {
	     try {
		driver.findElement(By.id(htmlId));
		return false;
	     } catch (NoSuchElementException e) {
		return true;
	     }
	}
     });
}
</pre>
<p>There are also some more exotic ones, that dig a bit deeper into the API, such as code to assert and dismiss an alert:</p>
<pre class="is_code">
protected final boolean assertAndDismissAlert(String alertText) {
     Alert alert = seleniumContext.getWebDriver().switchTo().alert();
     if (alert != null) {
	boolean ok = alertText.equals(alert.getText());
	alert.dismiss();
	return ok;
     } else {
	return false;
     }
}
</pre>
<p>Or to check whether an element has focus:</p>
<pre class="is_code">
protected final boolean hasFocus(WebElement element) {
     WebElement focusedElement = seleniumContext.getWebDriver()
          .switchTo().activeElement();
     return element.equals(focusedElement);
}
</pre>
<h3>Selenium WebDriver issues</h3>
<p>Since Selenium WebDriver is a rather new library, and its a rather ambitious project, I guess it is to be expected there are some issues with it. For example, there appear to be some irregularities between what a human user sees/can interact with in the browserwindow and what the driver sees/can interact with.</p>
<h4>a. Element is visible for user, but not for Selenium 2</h4>
<p>Selenium 2 takes some assumptions that do not always hold. For example, if a dom element has a height or width of 0, Selenium 2 thinks it is not visible/not clickable, while in reality, it often is. If an element has a css float attribute set, the height becomes 0 but the element is still visible.<br />
A workaround that usually works is to click an element inside the element that has height 0.</p>
<h4>b. Element not found</h4>
<p>There are differences in behavior between drivers. This problem is an intrinsic effect of the WebDriver using the browser native APIs.<br />
Because of this, different browserdrivers can react a bit different. For example, in Firefox, clicking on a span element within a link works, in IE6 it does not.<br />
Also, different browsers have slightly different xpath implementations, which is something to keep in mind too.</p>
<h4>c. Click problems</h4>
<p>Selenium sometimes doesn’t correctly click an element on certain browsers. This appears to be a viewport problem. If the element is not in the current browser view, Selenium2 does not scroll to the link, the click does not trigger anything, although no Exception is thrown. This can happen with other commands on WebElements too, such as when calling clear() on a input WebElement.</p>
<h4>d. Selenium 2 sometimes shows very specific OS/browser dependent behaviour</h4>
<p>The previous problem is probably a more specific case of this one, and we are giving another example of it here.<br />
With Firefox, unlike with IE, the driver always scrolls to the link first before it clicks it. However, once we had a bunch of tests failing for Firefox on Windows while all these tests were green for Firefox on Ubuntu.<br />
Apparently, there was some Javascript on the page that caused a breadcrumbs bar to scroll with the page. On Windows, to click on a link, the Selenium2 driver scrolled to the link(to make it visible) and then wanted to click on it. However, right after the scroll, the breadcrumbs appeared over the link to click, and by the time the driver simulated the click, the link was not clickable anymore so nothing happened and all the mentioned tests timed out.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.integratingstuff.com/2011/08/31/converting-selenium-1-to-selenium-2-tests/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Understanding and avoiding the Java Permgen Space error</title>
		<link>http://www.integratingstuff.com/2011/07/24/understanding-and-avoiding-the-java-permgen-space-error/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=understanding-and-avoiding-the-java-permgen-space-error</link>
		<comments>http://www.integratingstuff.com/2011/07/24/understanding-and-avoiding-the-java-permgen-space-error/#comments</comments>
		<pubDate>Sun, 24 Jul 2011 21:40:48 +0000</pubDate>
		<dc:creator>Steffen Luypaert</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[PermGen]]></category>

		<guid isPermaLink="false">http://www.integratingstuff.com/?p=424</guid>
		<description><![CDATA[In this article, I look into what it means when a Java program runs into a OutOfMemoryError: PermGen Space error. I first explain what the permanent generation heap space is, after which I explain the usual cause of the Permgen Space error and I give some pointers on how to avoid it.]]></description>
			<content:encoded><![CDATA[<p>In this article, I look into what it means when a Java program runs into a OutOfMemoryError: PermGen Space error. I first explain what the permanent generation heap space is, after which I explain the usual cause of the Permgen Space error and I give some pointers on how to avoid it.</p>
<h1>Introduction</h1>
<p>Usually, we do not look into JVM intrinsics. We take the JVM as is.<br />
Some of the world’s finest engineers are working on the JVM(s) and I am sure we can’t improve their work, definitely not without getting seriously involved.<br />
However, as a Java developer, you are often confronted with performance issues, usually working memory related.<br />
The problem I run into most is the dreaded OutOfMemoryError: PermGen Space error. I thought it would be nice to know more about it, so I looked into what causes it exactly.</p>
<h1>Java memory structure</h1>
<p>To understand the error, we have to look into how the jvm memory is structured.<br />
There are two memory regions in the JVM: the heap and the stack. Local variables and methods reside on the stack, everything else on the heap.<br />
This Java heap memory is structured again into regions, called generations. The longer an object lives, the higher the chance it will be promoted to an older generation. Young generations(such as Eden on Sun JVM) are more garbage collected than older generations(survivor and tenured on Sun JVM). However, there is also some separate heap space called permanent generation. Since it is a separate region, it is not considered part of the Java Heap space. Objects in this space are relatively permanent. Class definitions are stored here, as are static instances.</p>
<p>Without getting into details, <a href="http://download.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html">Classloader</a>s deploy and undeploy classes all the time. For example, this happens when an application is deployed or undeployed on a webserver. On web servers, all applications have their own Classloader. When an application is deployed or undeployed, its class definitions and Classloader are respectively put into and removed from the permanent generation heap. </p>
<h1>OutOfMemoryError: PermGen Space</h1>
<p>The OutOfMemoryError: PermGen Space error occurs when the permanent generation heap is full. Although this error can occur in normal circumstances, usually, this error is caused by a memory leak.</p>
<p>In short, such a memory leak means that a classloader and its classes cannot be garbage collected after they have been undeployed/discarded.</p>
<p>To give an example on how this can happen, let’s say we have a Payment class, which is part of a jar in a web application that is deployed on some webserver. In the lib folder of the web server, there is some logging framework present, which has a Log class with the method register(Class clazz) with which classes can be registered for logging. Let’s say that the Payment class gets registered by this method and the Log class starts keeping a reference to the clazz object. When the Payment class gets undeployed, it is still registered with the Log class. The Log class will still have a reference to it and hence, it will never be garbage collected. Moreover, since the Payment Class has a reference to its ClassLoader in turn, the ClassLoader itself will never be garbage collected either, and so will none of the classes it loaded.</p>
<p>An even more typical example is with the use of proxy objects. Spring and Hibernate often make proxies of certain classes. Such proxy classes are loaded by a classloader as well, and often, the generated class definitions – which are loaded like classes and stored in permanent generation heap space – are never discarded, which causes the permanent generation heap space to fill up.</p>
<h1>Avoiding the error</h1>
<h3>1. Increasing the maximum size of the permgen heap</h3>
<p>The first thing one can do is to make the size of the permanent generation heap space bigger.<br />
This cannot be done with the usual –Xms(set initial heap size) and –Xmx(set maximum heap size) JVM arguments, since as mentioned, the permanent generation heap space is entirely separate from the regular Java Heap space, and these arguments set the space for this regular Java heap space. However, there are similar arguments which can be used(at least with the Sun/OpenJDK jvms) to make the size of the permanent generation heap bigger:</p>
<pre class="is_code">
-XX:MaxPermSize=256m
</pre>
<p>would set its maximum size to 256m, which is 4 times bigger than the default size.</p>
<h3>2. Use common sense when using static fields on classes.</h3>
<p>Make sure you do not write classes that have static variables keeping references to class definitions and the like.</p>
<h3>Using JDK dynamic proxies instead of cglib proxies</h3>
<p><a href="http://loweringexpectations.nfshost.com/?tag=permgen">Some third party frameworks, such as cglib(although it might be better for newer versions of the library?), appear to be permgen monsters.</a></p>
<p>So using jdk dynamic proxies instead of cglib might be a good idea when getting the error.</p>
<p>Also, newer versions of Hibernate appear to not use cglib as a bytecode provider anymore, so upgrading your version of Hibernate, might drastically lower your chances on getting the error.</p>
<h1>Summary</h1>
<p>In general, when getting the error, one needs to determine why certain class definitions are not garbage collected. Once that is known, it should be possible to battle the error.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.integratingstuff.com/2011/07/24/understanding-and-avoiding-the-java-permgen-space-error/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Adding a hook to Liferay</title>
		<link>http://www.integratingstuff.com/2011/06/05/adding-a-hook-to-liferay/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=adding-a-hook-to-liferay</link>
		<comments>http://www.integratingstuff.com/2011/06/05/adding-a-hook-to-liferay/#comments</comments>
		<pubDate>Sun, 05 Jun 2011 21:39:23 +0000</pubDate>
		<dc:creator>Steffen Luypaert</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Liferay]]></category>
		<category><![CDATA[Portals]]></category>

		<guid isPermaLink="false">http://integratingstuff.com/?p=397</guid>
		<description><![CDATA[In this article, we elaborate on Liferay hooks, which are a type of plugin for <a href="http://www.liferay.com">the Java-based Liferay portal</a>. We discuss what they are and then we build one. We setup the project structure and add all the code. Then we deploy it to Liferay.]]></description>
			<content:encoded><![CDATA[<p>In this article, we elaborate on Liferay hooks, which are a type of plugin for <a href="http://www.liferay.com">the Java-based Liferay portal</a>. We discuss what they are and then we build one. We setup the project structure and add all the code. Then we deploy it to Liferay.</p>
<h1>Liferay hooks</h1>
<p>After playing around a bit with a portal, and maybe writing a few portlets for it, you probably start wondering how you can override some of the portal features. You might want to use a branded theme for the whole portal or integrate the portal login with your own database of users/own authentication system.<br />
Liferay has quite a few types of so called “plugins”, such as themes and layout templates, which enable developers to achieve the aforementioned effects. The most powerful type of plugin is called a “hook”. <a href="http://www.liferay.com/community/wiki/-/wiki/Main/Portal+Hook+Plugins">Liferay hooks</a> let you change the portal functionality itself.</p>
<div style="background:lightgreen;border:solid 1px black;padding:10px;">
Note: There is also the even more powerful, old EXT Liferay extension model. This EXT model has become rather unpopular during the last few years. It basically allows you to override parts /specific classes of the Liferay source code. Usually, EXT plugins are tied to one version of Liferay, since they rely on specific implementation classes of a particular build, instead of on interfaces that change a lot less. The only common case in which EXT is still used, is to override Liferay Struts action classes, like LoginAction and LayoutAction. However, as we will see, from the next versions of Liferay on, it will be possible to override these action classes with hooks as well, which will almost deprecate the EXT model in my opinion.
</div>
<h1 style="padding-top:15px;">Building a hook</h1>
<p>We are going to build a simple hook that logs info about every request to Liferay. Before and after every request we will log something. This could be a useful hook if it is necessary to log the ips of the clients or if one wants to calculate how long every Liferay request takes.</p>
<h3>Project Structure</h3>
<p>The project structure of a hook looks very similar to that of a a regular webapp. Hooks, just like portlets, are packaged as wars. You can even deploy portlets and hooks in the same war file, although this is conceptually not a good idea, because hooks override the behavior of the whole portal, and portlets are supposed to be small pluggable components that don’t impact the portal itself.</p>
<p>Since we are using Maven, we use a typical Maven project structure.</p>
<p>Note: It is possible to build hooks using the <a href="http://www.liferay.com/en/community/liferay-projects/liferay-ide/overview">Eclipse Liferay IDE</a> plugin, but for this article, we choose to explain building a hook from scratch.</p>
<p>The files in our hook war will be the following:</p>
<ul>
<li>/src/main/java/com/integratingstuff/liferay/hooks/CustomPostEventAction</li>
<li>/src/main/java/com/integratingstuff/liferay/hooks/CustomPreEventAction</li>
<li>/src/main/resources/portal-hook.properties</li>
<li>/src/main/webapp/WEB-INF/liferay-hook.xml</li>
<li>/src/main/webapp/WEB-INF/liferay-plugin-package.properties</li>
<li>/pom.xml</li>
</ul>
<p>We see that all the typical Maven directories: src/main/java for the Java files, src/main/resources for the resource file and src/main/webapp for the web content. The file that defines our hook, liferay-hook-xml, resides in this last directory.</p>
<p>Note: It is not necessary to have a web.xml file in your source code. Upon hook deploy however, Liferay will automatically create it for the deployed war.</p>
<h3>Defining hooks</h3>
<p>Hooks are defined in a liferay-hook.xml file.</p>
<p>The liferay-hook.xml for our portlet looks like this:</p>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
&lt;hook&gt;
	&lt;portal-properties&gt;portal-hook.properties&lt;/portal-properties&gt;
	&lt;!--
	&lt;custom-jsp-dir&gt;/WEB-INF/jsps&lt;/custom-jsp-dir&gt;	--&gt;
	&lt;struts-action&gt;
		&lt;struts-action-path&gt;/portal/layout&lt;/struts-action-path&gt;
		&lt;struts-action-impl&gt;com.integratingstuff.liferay.hooks.CustomLayoutAction&lt;/struts-action-impl&gt;
	&lt;/struts-action&gt;
	&lt;service&gt;
		&lt;service-type&gt;com.liferay.portal.service.UserLocalService&lt;/service-type&gt;
		&lt;service-impl&gt; com.integratingstuff.liferay.hooks.CustomUserLocalServiceImpl&lt;/service-impl&gt;
	&lt;/service&gt;
	 --&gt;
&lt;/hook&gt;
</pre>
<p>It is only overriding some portal properties, but for completeness, we also comment on the most common and important other uses of hooks in this section.</p>
<h4>Overriding portal properties</h4>
<p>We are only supplying a portal.properties file to override some properties of our portal(this can be done on portal level also, but note that pointing to hook classes in the actual portal.properties file is not a good idea since theses classes are not on the classpath of the portal itself).<br />
Not all portal properties can be overridden with a hook. You can read which properties can be overridden by taking a look at <a href="http://www.liferay.com/community/wiki/-/wiki/Main/Hook+DTD+-+6.0">the liferay hook dtd wiki page</a>.</p>
<h4>Overriding portal jsps</h4>
<p>There are some other tags that can be used in a liferay-hook.xml file.<br />
It is possible to override portal specific jsps by supplying a custom-jsp-dir and then putting jsps in that directory. For example, if we define a custom-jsp-dir &#8220;/WEB-INF/jsps&#8221; and then we create /WEB-INF/jsps/html/portlet/blogs/view.jsp in our project structure, this portal specific jsp will be overridden. The one from the hook will be used instead of the portal one.</p>
<h4>Overriding portal services</h4>
<p>Liferay contains a lot of services that are defined as spring beans in … of the portal source code. All these services can be overridden by using the service tag of liferay-hook.xml. For example, we could replace the portal implementation of the com.liferay.portal.service.UserLocalService – the service that enables one to save/update/lookup information about portal users &#8211; interface with our own this way.</p>
<h4>Soon: overriding actions</h4>
<p>As stated in t<a href="http://www.liferay.com/web/mika.koivisto/blog/-/blogs/7132115">his blog article</a>, in the next releases of Liferay, it will be possible to override Liferay Struts actions with hooks as well.</p>
<p>Differently from Liferay services, these Struts actions are not defined as Spring beans, and in the past, they required the EXT model to be overridden. These Struts Liferay actions are concrete classes, not interfaces, are part of the internal Liferay code and dependent heavily on its implementation. It often is not a good idea to override them, since changing your version of Liferay would likely break them, but still, it is not uncommon to override some of the important ones like LayoutAction(to get custom rendering behavior of any page fragment) and LoginAction(to get custom portal login functionality that can not be covered by the Liferay event model) if need be. </p>
<h4>Soon: adding servlet filters</h4>
<p>Apparently, soon it will also become possible to add servlet-filter and servlet-mapping declarations to your hook, making it possible to add custom servlet filters to Liferay itself.</p>
<h3>Our hook</h3>
<p>Our portal-hook.properties file looks like this:</p>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
servlet.service.events.pre=com.integratingstuff.liferay.hooks.CustomPreEventAction
servlet.service.events.post=com.integratingstuff.liferay.hooks.CustomPostEventAction
</pre>
<p>Basically, we are adding events before Liferay starts to process a request(but after any Liferay servlet filter) and after the processing of every request.</p>
<p>As stated in the comments of the portal.properties documentation(http://www.liferay.com/community/wiki/-/wiki/Main/Portal+Properties+6.0.5), when overriding Portal Events properties, we have to point to classes that extend com.liferay.portal.kernel.events.Action.</p>
<p>So when implementing these classes, we have to extend this Action class:</p>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
public class CustomPreEventAction extends Action{
	public void run(HttpServletRequest request, HttpServletResponse response)
			throws ActionException {
		log.info(“Request from ip: ” + request.getRemoteAddr();
		request.setAttribute(“startOfRequest”,new Date());
	}
}

public class CustomPostEventAction extends Action{
	public void run(HttpServletRequest request, HttpServletResponse response)
			throws ActionException {
		Date now = new Date();
		Date startOfRequest = request.getAttribute(“startOfRequest”);
		if (startOfRequest != null){
			log.info(“Request ook: ” + (now.getTime() - startOfRequest.getTime())+ “ms”);
		}
	}
}
</pre>
<p>Note though that the request and response that are passed in these actions are actually wrapper objects that are managed by Liferay itself.<br />
Some things cannot be done with these wrapper objects. Invalidating or modifying the session of the request for example.</p>
<h3>Other files</h3>
<h4>Optional: liferay-plugin-package.properties</h4>
<p>There is also the optional liferay-plugin-package.properties file. It is good practice to add it. It also offers some extra features.</p>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
name=IntegratingStuffDemo-hook
module-group-id=liferay-ee
module-incremental-version=1
tags=
short-description=
change-log=
page-url=http://www.liferay.com
author=Liferay, Inc.
licenses=EE

#portal.dependency.jars=portal-impl.jar, struts.jar, commons-logging.jar, log4j.jar, slf4j-api.jar, slf4j-log4j12.jar, commons-codec.jar
</pre>
<p>Notice the commented line. With portal.dependency.jars you can supply jars that have to be copied from the portal root to the lib folder of the hook. This way, you can easily write a hook that depends on the portal implementation for example(for example, if you want to override one of the Struts actions that are part of portal-impl, with struts-action).</p>
<p>Even if you do not specify any portal.dependency.jars, Liferay will still copy its log4j.jar, commons-logging.jar and util-java.jar into the lib folder of the hook. Adding these this way(or having it in the lib folder of the war) is not necessary. </p>
<h4>pom.xml</h4>
<p>If you are using Maven, you will need the following dependencies in your pom.xml file, of which some are Liferay specific:</p>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
&lt;dependency&gt;
   &lt;groupId&gt;com.liferay.portal&lt;/groupId&gt;
   &lt;artifactId&gt;util-java&lt;/artifactId&gt;
   &lt;version&gt;6.0.5&lt;/version&gt;
   &lt;scope&gt;provided&lt;/scope&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
   &lt;groupId&gt;com.liferay.portal&lt;/groupId&gt;
   &lt;artifactId&gt;portal-service&lt;/artifactId&gt;
   &lt;version&gt;6.0.5&lt;/version&gt;
   &lt;scope&gt;provided&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
   &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
   &lt;artifactId&gt;servlet-api&lt;/artifactId&gt;
   &lt;version&gt;2.5&lt;/version&gt;
   &lt;scope&gt;provided&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
   &lt;groupId&gt;javax.servlet.jsp&lt;/groupId&gt;
   &lt;artifactId&gt;jsp-api&lt;/artifactId&gt;
   &lt;version&gt;2.1&lt;/version&gt;
   &lt;scope&gt;provided&lt;/scope&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
   &lt;groupId&gt;javax.portlet&lt;/groupId&gt;
   &lt;artifactId&gt;portlet-api&lt;/artifactId&gt;
   &lt;version&gt;2.0&lt;/version&gt;
   &lt;scope&gt;provided&lt;/scope&gt;
&lt;/dependency&gt;
</pre>
<h1>Deploying the hook</h1>
<p>Deploying the hook is very straightforward. You can just drop the resulting war in the deploy directory of your Liferay install or add it to your server in your Eclipse/other IDE.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.integratingstuff.com/2011/06/05/adding-a-hook-to-liferay/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Getting started with portals: writing a portlet with Spring Portlet Mvc and deploying it to a portal</title>
		<link>http://www.integratingstuff.com/2011/05/29/getting-started-with-portals-writing-a-portlet-with-spring-portlet-mvc-and-deploying-it-to-a-portal/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=getting-started-with-portals-writing-a-portlet-with-spring-portlet-mvc-and-deploying-it-to-a-portal</link>
		<comments>http://www.integratingstuff.com/2011/05/29/getting-started-with-portals-writing-a-portlet-with-spring-portlet-mvc-and-deploying-it-to-a-portal/#comments</comments>
		<pubDate>Sun, 29 May 2011 18:52:57 +0000</pubDate>
		<dc:creator>Steffen Luypaert</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Liferay]]></category>
		<category><![CDATA[Portals]]></category>
		<category><![CDATA[Spring]]></category>

		<guid isPermaLink="false">http://integratingstuff.com/?p=358</guid>
		<description><![CDATA[In this article, we first talk about portals and when they should be used. Then we talk about Java portlets and their specification, after which we write a portlet with <a href="http://static.springsource.org/spring/docs/3.0.x/reference/portlet.html">Spring Portlet Mvc</a>. Finally, we deploy the portlet we wrote on <a href="http://www.liferay.com">Liferay</a>, the leading Java portal.]]></description>
			<content:encoded><![CDATA[<p>In this article, we first talk about portals and when they should be used. Then we talk about Java portlets and their specification, after which we write a portlet with <a href="http://static.springsource.org/spring/docs/3.0.x/reference/portlet.html">Spring Portlet Mvc</a>. Finally, we deploy the portlet we wrote on <a href="http://www.liferay.com">Liferay</a>, the leading Java portal.</p>
<h1>Portals</h1>
<p>A portal is a collection of windowed mini web applications, called portlets, which support features like personalization, content aggregation, integration into a foreign portal, authentication and customization. The portal itself usually offers things like an eventing system, single sign on, a portal search, tons of community stuff and facilitates easy communication between the different windows/portlets.</p>
<p>In the screenshot below we see an example of a portal page. Notice all the different windows, each one representing a small web application.</p>
<p><a href="http://integrating.itfrog.org/wp-content/uploads/2011/05/liferay1.png"><img src="http://integrating.itfrog.org/wp-content/uploads/2011/05/liferay1.png" alt="" title="Some portlets" width="630" height="299" class="aligncenter size-full wp-image-359" /></a></p>
<div style="background:lightgreen;border:solid 1px black;padding:10px;">
Note: Our definition is the definition of an enterprise portal. A web portal is something different. The latter is just a portal to other web pages and does not necessarily have the windowed mini web applications, and could in theory be just a long list of external links how unsexy that may be.
</div>
<h3 style="padding-top:15px;">Popularity</h3>
<p>Some years ago, portals were heavily hyped, as the future of the web even, but these days, not anymore. I even met some people who think they are plain useless and should be buried. Then I met some enthusiasts again who see more uses for them than I do.</p>
<h3>Uses</h3>
<h4>Community websites</h4>
<p>A portal like <a href="http://www.liferay.com/products/liferay-portal/features/portal">Liferay offers tons of features out of the box</a>. If you need an instant messaging system, wikis, forums, message boards, document management, auditing, polls, a chat system, friends lists, blogs and calendars, combined with custom development, Liferay is probably one of the best choices around. You definitely do not want to start building all these things &#8211; which have been implemented a 1000 times before &#8211; yourself. You could use seperate packages, such as JForum or something, but these would still need a lot of integration and custom work. You could go with a PHP solution such as Drupal or WordPress, but if you are a Java developer/member of a team of Java developers, you are probably not a fan of doing all the custom development in php. Hence, Liferay.<br />
To put it bold, if I would have to make a Facebook, LinkedIn or Youtube, I would make it with Liferay.<br />
Let&#8217;s hope people are not going to fire load issue questions at me now. If they do, I am just going to point them to the <a href="http://www.liferay.com/documentation/additional-resources/whitepapers">Liferay Performance whitepapers</a>.</p>
<p>Since I am on a portal/Liferay marketing roll anyway, people can view a<a href="http://www.liferay.com/web/ronald.sarayudej/blog/-/blogs/8785986"> list of sites that use Liferay here</a>. Unlike what people might be thinking by now, I am not getting paid by Liferay for this article.</p>
<h4>Enterprise websites backed by a SOA</h4>
<p>But what if you do not need all the community stuff?<br />
In which case is using the portlet specification over the servlet specification useful by nature, without having to fallback on other portal features to defend its use?<br />
This can only be when all the seperate portlets add value over building the solution as a few classic web applications. If one portlet allows to select a customer, some other portlets could be fed this selection as input for example. This beats making a view interface to get a customer for every seperate web application.<br />
Unfortunately, this will only work when there is already a clean modularization on the backend. If the backend consists of one database and one huge model, things will get too intertwined, and such modularization on the view level will become a headache instead of an advantage.<br />
This is why I think an enterprise portal needs to be backed by a service oriented architecture, in which there are clearly seperated services which should be integrated with each other. The power of portlets comes from the ease with which they integrate disparate sources. So, they should be used when there is not one source of content but when there are multiple sources of content.</p>
<h3>When not to use</h3>
<p>Most web applications do not need a community and most web applications are (arguably) not an integration of many disparate content sources. Hence, in my opinion, portals are rather a niche product than a good overall solution.</p>
<p>Some argue that portals offer another level of abstraction over web development and that more things are taken care of for a developer. Sure, but at the same time, the developer is confronted with a lot more complexity, is constrained more and in my experience, many portal projects end up modifying the portal software itself which ties the developed software to some particular portal, destroying the portability argument. For Liferay for example, people usually rely on the Liferay specific window state &#8220;exclusive&#8221; for ajax requests, use the Liferay specific action-url-redirect to apply the post-redirect-get pattern and when custom authentication logic is necessary,  Liferay hooks are built which rely on specific Liferay api. Sometimes you wonder why there is only a portlet spec and not a portal spec.</p>
<p>Portals are not a one-size-fits-all solution. If you do not have the feeling a portal is a perfect fit for your needs, you probably should not use one.</p>
<h1>Portlets</h1>
<p>We know now what a portlet is. A mini web application. A window on a portal page. But we didn&#8217;t dive into the technical details until now.</p>
<h3>The portlet container</h3>
<p>In short, the portal utilizes a portlet container to manage the lifecycle of the portlets just like a servlet container is used to manage the lifecycle of servlets. A portlet container is responsible for the initialization, request processing and destruction of portlets. The Java Portlet Specification defines the contract between a compliant portlet container and portlets. This standardization allows for portability of portlets between portal implementations.</p>
<h3>Portlet versus servlet development</h3>
<p>Portlet development is very similar to servlet development. The portlet API is modeled after the servlet API. The Portlet, PortletContext, PortletRequest and PortletResponse are very similar to their servlet counterparts. The major difference is that portlets only render a fragment of an html page, instead of a whole page.</p>
<h3>The portlet specification</h3>
<p>However, there still are some differences between portlet and servlet development.<br />
We discuss the 3 most important portlet specific features now.</p>
<h4>a. Different phases</h4>
<p>With portlets, a request has at least two distinct phases: the action phase and the render phase. The action phase is executed only once. This is the moment where any backend actions occur. In the render phase the view is rendered to the user. Unlike the action phase, the render phase can be executed multiple times for a single request. With servlets, there is no such distinction on the API level, and these phases is something a portlet developer has to get used to.</p>
<h4>b. Portlet modes</h4>
<p>A portlet can have different display modes. The portlet mode determines what content the portlet should generate. The Portlet API defines 3 portlet modes: view, edit and help. In view mode, a user typically views data. In edit mode, a user typically modifies data. In help mode, a user can consult help about the portlet. A portlet developer can add any number of custom portlet modes to a portlet.</p>
<h4>c. Window states</h4>
<p>A window state indicates the amount of portal page space that should be assigned to a portlet. The portlet API defines 3 window states: normal, minimized and maximized. Any portal is allowed to define additional window states. Liferay, for example, has one additional window state, &#8220;exclusive&#8221;, which just renders the page fragment coming from the portlet, without decorating it with the entire portal page. Very useful when there is a need for Ajax integration.</p>
<p>In the screenshot below, we see the portal page with the portlets again. This time however, some of them have the portlet window state &#8220;minimized&#8221;, the others still have window state &#8220;normal.&#8221; Note that every portlet has some icons. These will either change the window state(to maximize it for example), or they will change the portlet mode, from &#8220;view&#8221; mode to &#8220;edit&#8221; mode for example.</p>
<p><a href="http://integrating.itfrog.org/wp-content/uploads/2011/05/liferay2.png"><img src="http://integrating.itfrog.org/wp-content/uploads/2011/05/liferay2.png" alt="" title="Portlet Modes and Window States" width="630" height="282" class="aligncenter size-full wp-image-362" /></a></p>
<div style="background:lightgreen;border:solid 1px black;padding:10px;">
Note: There are two portlet specifications. JSR186(portlet 1.0 api) and JSR286(portlet 2.0 api). In JSR286, a lot of shortcomings of JSR186, which made writing vendor-neutral portlets difficult/limited, were lifted. The most important JSR286 new features are interportlet communication(next to the action and render phases, there is also an event phase in JSR286), WSRP 2.0 alignment, support for Ajax, and portlet filters and listeners. In my opinion, JSR286 was a big step in the good direction, but it definitely did not solve all common cases in which vendor-specific api is necessary.</div>
<h1 style="padding-top:15px;">Writing a portlet</h1>
<h2>Spring Portlet Mvc</h2>
<p>The <a href="http://static.springsource.org/spring/docs/3.0.x/reference/portlet.html">Spring Portlet Mvc framework</a> is a mirror image of the <a href="http://static.springsource.org/spring/docs/3.0.x/reference/mvc.html">Spring Web Mvc framework</a>, and uses the same underlying view abstractions and integration technology. Therefore, different Web Mvc classes will be reused.<br />
It is also important to realize that when using Spring Portlet Mvc, you will no longer write your own Portlet but use the Spring Mvc one(just like you dont write your own servlets when using Spring Web Mvc).</p>
<h2>Project structure</h2>
<p>We are going to create one of the simplest Spring Portlet Mvc portlets possible.</p>
<p>The files we will need are the following:</p>
<ul>
<li>/src/main/java/com/integratingstuff/portlets/test/SampleController</li>
<li>/src/main/webapp/WEB-INF/portlet.xml</li>
<li>/src/main/webapp/WEB-INF/springContextConfig.xml</li>
<li>/src/main/webapp/WEB-INF/web.xml</li>
<li>/src/main/webapp/WEB-INF/jsp/demo.jsp</li>
<li>/pom.xml</li>
</ul>
<p>In comparison with a regular webapp, there is only one portlet specific file: portlet.xml.</p>
<h3>portlet.xml</h3>
<p>The portlet.xml file is our most important file. It defines our portlet.</p>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
&lt;portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
	version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"&gt;

	&lt;portlet&gt;
		&lt;portlet-name&gt;sample&lt;/portlet-name&gt;
		&lt;portlet-class&gt;org.springframework.web.portlet.DispatcherPortlet&lt;/portlet-class&gt;
		&lt;init-param&gt;
			&lt;name&gt;contextConfigLocation&lt;/name&gt;
			&lt;value&gt;
				/WEB-INF/springContextConfig.xml
			&lt;/value&gt;
		&lt;/init-param&gt;
		&lt;supports&gt;
			&lt;mime-type&gt;text/html&lt;/mime-type&gt;
			&lt;portlet-mode&gt;view&lt;/portlet-mode&gt;
		&lt;/supports&gt;
		&lt;portlet-info&gt;
			&lt;title&gt;Portlet Mvc Demo&lt;/title&gt;
		&lt;/portlet-info&gt;
	&lt;/portlet&gt;

&lt;/portlet-app&gt;
</pre>
<p>Portlet Mvc is designed around a portlet that dispatches requests to spring portlet mvc controllers. However, the <a href="http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/web/portlet/DispatcherPortlet.html">DispatcherPortlet</a> does more than only that. It also makes sure that the portlet is completely integrated with the Spring ApplicationContext and the developer is able to use every other Spring feature.</p>
<p>You will probably notice that we, in this tutorial, are developing a portlet that only supports the “view” portlet mode.</p>
<p>Note also how we point the portlet to our spring contextConfigLocation. If we would not add this init-param, the DispatcherPortlet will look for the default [portlet-name]-portlet.xml file in the WEB-INF directory. If this file would not be present, an exception would be thrown at deploy time.</p>
<h3>springContextConfig.xml</h3>
<p>On initialization of the DispatcherPortlet, the framework will create the bean definitions defined in this file.</p>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"
	&gt;

	&lt;bean id="sampleController" class="com.integratingstuff.portlets.test.SampleController"&gt;
	&lt;/bean&gt;

	&lt;bean id="portletModeHandlerMapping" class="org.springframework.web.portlet.handler.PortletModeHandlerMapping"&gt;
	    &lt;property name="portletModeMap"&gt;
	        &lt;map&gt;
	        	&lt;entry key="view" value-ref="sampleController"/&gt;
	        &lt;/map&gt;
	    &lt;/property&gt;
	&lt;/bean&gt;

	&lt;bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver"&gt;
	    &lt;property name="prefix" value="/WEB-INF/jsp/"/&gt;
	    &lt;property name="suffix" value=".jsp"/&gt;
	    &lt;property name="viewClass"&gt;&lt;value&gt;org.springframework.web.servlet.view.JstlView&lt;/value&gt;&lt;/property&gt;
	&lt;/bean&gt;

&lt;/beans&gt;
</pre>
<p>We have one custom controller, which we will discuss later.<br />
Note how this controller is mapped on the view portlet mode in the <a href="http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/web/portlet/handler/PortletModeHandlerMapping.html">PortletModeHandlerMapping</a> bean, which is the bean the DispatcherPortlet will use to decide which controller to execute.</p>
<p>The last bean to discuss is our viewResolver. Note that this bean is a member of the regular spring mvc framework and not a member of a portlet specific package. This is because spring portlet mvc reuses all spring mvc view technologies, as we already said when we introduced Spring Portlet mvc. How this is possible is discussed in the next section.</p>
<h3>web.xml</h3>
<p>We also need a valid web.xml file:</p>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"&gt;

	&lt;display-name&gt;portletMvcDemo&lt;/display-name&gt;

	&lt;servlet&gt;
		&lt;servlet-name&gt;ViewRendererServlet&lt;/servlet-name&gt;
		&lt;servlet-class&gt;org.springframework.web.servlet.ViewRendererServlet&lt;/servlet-class&gt;
	&lt;/servlet&gt;
	&lt;servlet-mapping&gt;
		&lt;servlet-name&gt;ViewRendererServlet&lt;/servlet-name&gt;
		&lt;url-pattern&gt;/WEB-INF/servlet/view&lt;/url-pattern&gt;
	&lt;/servlet-mapping&gt;

&lt;/web-app&gt;
</pre>
<p>When using Spring Portlet mvc, this file will always declare the ViewRendererServlet. To be able to reuse all the view technologies from Spring Web Mvc, the PortletRequest and Response need to be converted to a HttpServletRequest and Response in order to execute the render method of the (regular Spring Web Mvc) View. In order to do this, DispatcherPortlet uses the special <a href="http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/web/servlet/ViewRendererServlet.html">ViewRendererServlet</a> which only exists for this purpose.</p>
<h3>SampleController</h3>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
package com.integratingstuff.portlets.test;

import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import org.springframework.web.portlet.ModelAndView;
import org.springframework.web.portlet.mvc.AbstractController;

public class SampleController extends AbstractController {

	public ModelAndView handleRenderRequestInternal(RenderRequest request, RenderResponse response) throws Exception {
		ModelAndView mav = new ModelAndView("demo");
		mav.addObject("message", "Check it out!");
		return mav;
	}

}
</pre>
<p>Note that there are actually two methods to implement for our subclass of <a href="http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/web/portlet/mvc/AbstractController.html">AbstractController</a>: handleActionRequestInternal and handleRenderRequestInternal. We are not doing any backend action, we are just building and rendering the view, hence we only override the handleRenderRequestInternal method.</p>
<h3>demo.jsp</h3>
<p>We are returning a &#8220;demo&#8221; view from our controller, which our viewResolver resolves to the following demo.jsp:</p>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
&lt;%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%&gt;
This is our test portlet.&lt;br/&gt;
Message: ${message}
</pre>
<p>This is just a regular jsp.</p>
<h3>pom.xml</h3>
<p>This project was developed as a Maven project. This Maven pom.xml file is not essential for portlets at all, but for the people copy pasting along, the pom is printed here for easy reference:</p>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
	&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
	&lt;groupId&gt;com.integratingstuff.portlets.test&lt;/groupId&gt;
	&lt;artifactId&gt;portletMvcDemo&lt;/artifactId&gt;
	&lt;version&gt;1.0&lt;/version&gt;
	&lt;packaging&gt;war&lt;/packaging&gt;
	&lt;dependencies&gt;

		&lt;dependency&gt;
			&lt;groupId&gt;javax.servlet&lt;/groupId&gt;
			&lt;artifactId&gt;servlet-api&lt;/artifactId&gt;
			&lt;version&gt;2.5&lt;/version&gt;
			&lt;scope&gt;provided&lt;/scope&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;javax.servlet.jsp&lt;/groupId&gt;
			&lt;artifactId&gt;jsp-api&lt;/artifactId&gt;
			&lt;version&gt;2.1&lt;/version&gt;
			&lt;scope&gt;provided&lt;/scope&gt;
		&lt;/dependency&gt;

		&lt;dependency&gt;
			&lt;groupId&gt;junit&lt;/groupId&gt;
			&lt;artifactId&gt;junit&lt;/artifactId&gt;
			&lt;version&gt;4.8.2&lt;/version&gt;
			&lt;scope&gt;test&lt;/scope&gt;
		&lt;/dependency&gt;

		&lt;dependency&gt;
			&lt;groupId&gt;org.slf4j&lt;/groupId&gt;
			&lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
			&lt;version&gt;1.6.1&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;org.slf4j&lt;/groupId&gt;
			&lt;artifactId&gt;slf4j-log4j12&lt;/artifactId&gt;
			&lt;version&gt;1.6.1&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;commons-collections&lt;/groupId&gt;
			&lt;artifactId&gt;commons-collections&lt;/artifactId&gt;
			&lt;version&gt;3.2.1&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;commons-digester&lt;/groupId&gt;
			&lt;artifactId&gt;commons-digester&lt;/artifactId&gt;
			&lt;version&gt;2.1&lt;/version&gt;
		&lt;/dependency&gt;

		&lt;dependency&gt;
			&lt;groupId&gt;org.springframework&lt;/groupId&gt;
			&lt;artifactId&gt;spring-web&lt;/artifactId&gt;
			&lt;version&gt;3.0.5.RELEASE&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;org.springframework&lt;/groupId&gt;
			&lt;artifactId&gt;spring-webmvc-portlet&lt;/artifactId&gt;
			&lt;version&gt;3.0.5.RELEASE&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;org.springframework&lt;/groupId&gt;
			&lt;artifactId&gt;spring-test&lt;/artifactId&gt;
			&lt;version&gt;3.0.5.RELEASE&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;org.springframework.webflow&lt;/groupId&gt;
			&lt;artifactId&gt;spring-webflow&lt;/artifactId&gt;
			&lt;version&gt;2.2.1.RELEASE&lt;/version&gt;
			&lt;exclusions&gt;
				&lt;exclusion&gt;
					&lt;groupId&gt;org.springframework.webflow&lt;/groupId&gt;
					&lt;artifactId&gt;spring-js-resources&lt;/artifactId&gt;
				&lt;/exclusion&gt;
			&lt;/exclusions&gt;
		&lt;/dependency&gt;

		&lt;dependency&gt;
			&lt;groupId&gt;javax.servlet&lt;/groupId&gt;
			&lt;artifactId&gt;jstl&lt;/artifactId&gt;
			&lt;version&gt;1.1.2&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;taglibs&lt;/groupId&gt;
			&lt;artifactId&gt;standard&lt;/artifactId&gt;
			&lt;version&gt;1.1.2&lt;/version&gt;
		&lt;/dependency&gt;

		&lt;dependency&gt;
			&lt;groupId&gt;javax.portlet&lt;/groupId&gt;
			&lt;artifactId&gt;portlet-api&lt;/artifactId&gt;
			&lt;version&gt;2.0&lt;/version&gt;
			&lt;scope&gt;provided&lt;/scope&gt;
		&lt;/dependency&gt;

	&lt;/dependencies&gt;

&lt;/project&gt;
</pre>
<p>We can now build our portlet. If we run &#8220;maven package&#8221; on our project, a war is generated which is deployable on a portal.</p>
<h3>Optional: liferay-portlet.xml</h3>
<p>Usually, a liferay portlet also has a <a href="http://content.liferay.com/4.3/doc/devel/liferay_4_portlet_development_guide/multipage/ch02.html">liferay-portlet.xml</a>. This is only really necessary if you want to use some Liferay specific features, but when using Liferay, it always is good practice to include. If you want, add the following liferay-portlet.xml to your portlet application:</p>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 6.0.0//EN" "http://www.liferay.com/dtd/liferay-portlet-app_6_0_0.dtd" &gt;
&lt;liferay-portlet-app&gt;
	&lt;portlet&gt;
		&lt;portlet-name&gt;sample&lt;/portlet-name&gt;
		&lt;action-url-redirect&gt;false&lt;/action-url-redirect&gt;
	&lt;/portlet&gt;
&lt;/liferay-portlet-app&gt;
</pre>
<h1>Deploying the portlet to Liferay</h1>
<h3>1. Download Liferay</h3>
<p>First, download Liferay Community Edition 6.0 from <a href="http://www.liferay.com/downloads">http://www.liferay.com/downloads</a>. We use the version bundled with Tomcat. Extract the downloaded zip to your harddrive.</p>
<h3>2. Install the Eclipse Liferay IDE</h3>
<p>Although it is not necessary to use the Liferay IDE for Eclipse, we recommend it in this article. You can read about it at <a href="http://www.liferay.com/en/community/liferay-projects/liferay-ide/overview">Liferay IDE Overview</a> and figure out how to install it at the <a href="http://www.liferay.com/community/wiki/-/wiki/Main/Liferay+IDE+Installation+Guide">Liferay IDE Installation Guide</a>.</p>
<p>In short, you just have to add http://releases.liferay.com/tools/ide/eclipse/helios/stable/ as an update site within Eclipse(Help&gt;Install New Software in Eclipse) and install the plugins present on that location.</p>
<p>Note: There is also a Liferay Developer Studio, which is a bit more extended than the regular Liferay IDE, but for the purpose of this article, the Liferay IDE suffices.</p>
<h3>Add the Liferay Server to the Eclipse Servers panel</h3>
<p>Open the Eclipse Servers panel(Window&gt;Show View&gt;Servers) and rightclick in this window. Choose New&gt;Server, open the map &#8220;Liferay, Inc.&#8221; and select Liferay v6.0 CE Server(Tomcat 6) as Server Type. Click next and enter the Liferay tomcat directory: point to the tomcat within your extracted Liferay installation. Add the server.</p>
<p><a href="http://integrating.itfrog.org/wp-content/uploads/2011/05/liferay_ide_server.png"><img src="http://integrating.itfrog.org/wp-content/uploads/2011/05/liferay_ide_server.png" alt="" title="Liferay IDE Server" width="630" height="399" class="aligncenter size-full wp-image-369" /></a></p>
<h3>Start the server</h3>
<p>Rightclick on the server and press &#8220;Start&#8221;. The server will now be started and you will be able to access the portal in your browser through <a href="http://localhost:8080">http://localhost:8080</a></p>
<p>Note: If you have trouble starting the server, add the following vm arguments -Xms1024M -Xmx1024M -XX:MaxPermSize=256M and increase the server timeout(double click on the server in the Servers window).</p>
<h3>Add the portlet to the server</h3>
<p>Rightclick on the server again and choose &#8220;Add and Remove&#8221;. You can now choose to add any projects eligible to be deployed on the server(if your project is not, you probably need to add the dynamic web module Eclipse facet to your project). Our portlet project should be in the list under &#8220;Available&#8221;. Move it to the server(&#8220;Configured&#8221;). It should automatically be published(if not, rightclick on the server again and press &#8220;Publish&#8221;).</p>
<div style="background:lightgreen;border:solid 1px black;padding:10px;">
Note: Alternatively, if you are not using Eclipse for example, you can run the maven package goal on the project and put the resulting war in the deploy dir of the extracted Liferay installation. Then run the Liferay server by running &#8220;startup&#8221; in the bin folder of the tomcat folder of the Liferay folder.
</div>
<h3 style="padding-top:15px;">Add the portlet to a portal page</h3>
<p>Now Liferay is started, log on to Liferay(test@liferay.com with password test is the default test user on Liferay 6.0) and choose Add on the menu in the top, then &gt;More&gt;Undefined&gt;our portlet. The portlet we made will then be added to the page.</p>
<p><a href="http://integrating.itfrog.org/wp-content/uploads/2011/05/finished_portlet.png"><img src="http://integrating.itfrog.org/wp-content/uploads/2011/05/finished_portlet.png" alt="" title="Deployed portlet" width="630" height="294" class="aligncenter size-full wp-image-370" /></a></p>
<p>The portlet we developed is now deployed and visible on a portal. See how we already edited its background by using the portal &#8220;Look and Feel&#8221; feature.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.integratingstuff.com/2011/05/29/getting-started-with-portals-writing-a-portlet-with-spring-portlet-mvc-and-deploying-it-to-a-portal/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Migrating from Spring Security 2 to Spring Security 3</title>
		<link>http://www.integratingstuff.com/2011/04/30/migrating-from-spring-security-2-to-spring-security-3/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=migrating-from-spring-security-2-to-spring-security-3</link>
		<comments>http://www.integratingstuff.com/2011/04/30/migrating-from-spring-security-2-to-spring-security-3/#comments</comments>
		<pubDate>Sat, 30 Apr 2011 08:28:28 +0000</pubDate>
		<dc:creator>Steffen Luypaert</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[Spring Security]]></category>

		<guid isPermaLink="false">http://integratingstuff.com/?p=338</guid>
		<description><![CDATA[Spring Security 3 offers a number of improvements over Spring Security 2, such as the addition of Spring EL expressions in access declarations and better support for session management. Spring Security 3 also introduces a number of changes such as the removal of the NtmlFilter.

In most cases, a migration from Spring Security 2 to Spring Security 3 is rather straightforward, but when custom spring security filters are present, additional work needs to be done.

In this article we discuss all changes required to do the migration. We also recommend the order in which the changes are discussed as the order in which to do them.]]></description>
			<content:encoded><![CDATA[<p>Spring Security 3 offers a number of improvements over Spring Security 2, such as the addition of Spring EL expressions in access declarations and better support for session management. Spring Security 3 also introduces a number of changes such as the removal of the NtmlFilter.</p>
<p>In most cases, a migration from Spring Security 2 to Spring Security 3 is rather straightforward, but when custom spring security filters are present, additional work needs to be done.</p>
<p>In this article we discuss all changes required to do the migration. We also recommend the order in which the changes are discussed as the order in which to do them.</p>
<h1>1. Importing the dependencies</h1>
<p>One of the biggest overall changes between Spring Security 2 and Spring Security 3 is that Spring Security 3 takes a more modular approach. Spring Security 3 was divided into modules. The modules you will encounter in pretty much any webapp that is secured with spring-security are:</p>
<ol>
<li>spring-security-core(core classes such as Authentication, Voter, and the like)</li>
<li>spring-security-web(all core classes that are dependent on servlet api, such as all spring security filters)</li>
<li>spring-security-config(needed to use the spring security xml namespace in spring applicationContext files)</li>
</ol>
<p>There are lot of other specific modules, such as spring-security-ldap and spring-security-asm.</p>
<p>A maven dependency such as</p>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
&lt;artifactId&gt;spring-security-core&lt;/artifactId&gt;
&lt;version&gt;2.0.6.RELEASE&lt;/version&gt;
&lt;/dependency&gt;
</pre>
<p>would change to the following dependencies:</p>
<pre style="background:#efefef;border:1px solid #A6B0BF;overflow:auto;color:#000000;padding:10px;">
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
&lt;artifactId&gt;spring-security-core&lt;/artifactId&gt;
&lt;version&gt;3.0.5.RELEASE&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
&lt;artifactId&gt;spring-security-web&lt;/artifactId&gt;
&lt;version&gt;3.0.5.RELEASE&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
&lt;artifactId&gt;spring-security-config&lt;/artifactId&gt;
&lt;version&gt;3.0.5.RELEASE&lt;/version&gt;
&lt;/dependency&gt;
</pre>
<h1>2. Changing the classnames</h1>
<p>One of the effects of the modularisation is that a lot of spring security framework classes were moved to a more specific package. </p>
<p>SecurityContextHolder’s package changed from <a href="http://static.springsource.org/spring-security/site/docs/2.0.x/apidocs/org/springframework/security/context/package-summary.html">org.springframework.security.context</a> to <a href="http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/core/context/package-summary.html">org.springframework.security.core.context</a> for example. </p>
<p>In order to do a successful migration, it is necessary to change the import declaration of all spring security classes in classes that make use of them. </p>
<h1>3. Handling small changes to the API</h1>
<p>Usually, just changing the imported spring security class’s package name is enough. However, the api of some spring security framework classes was changed as well. Usually, these changes are rather small and easy to deal with.</p>
<p>Some of these small changes that come to mind:</p>
<ul>
<li>SpringSecurityException does not exist any longer. The quickest migration is to let it extend from RuntimeException or NestedRuntimeException instead of SpringSecurityException. Note that the specific Spring Security exceptions, such as AccessDeniedException and AuthenticationException still exist, but just extend RuntimeException directly now.</li>
<li>The decide method of AccessDecisionVoter now takes a collection of ConfigAttribute instead of a ConfigAttributeDefinition. ConfigAttributeDefinition was basically just a decorator for a collection of ConfigAttribute elements.</li>
<li>SavedRequest is now an interface instead of a concrete class. In order to instantiate a default implementation, instantiate DefaultSavedRequest.</li>
<li>The default User of the UserDetails interface does not have a setAuthorities method anymore. The authorities are passed to the constructor, in the implementation the variable is final.</li>
</ul>
<h1>4. Rewriting the filters</h1>
<p>Usually, a migration from Spring Security 2 to Spring Security 3 is straightforward. However, if you implemented custom spring security filters, chances are you will have a bit more work. The basic filter class from the Spring Security specific <a href="http://static.springsource.org/spring-security/site/docs/2.0.x/apidocs/org/springframework/security/ui/SpringSecurityFilter.html">SpringSecurityFilter</a> changed to the generic Spring web <a href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/filter/GenericFilterBean.html">GenericFilterBean</a> and the API of this class changed a lot more than the API of other classes.</p>
<p>Especially AbstractProcessingFilter(now AbstractAuthenticationProcessingFilter) changed. In Spring Security 3.0, the  determineFailureUrl and determineTargetUrl methods were removed in favour of adding separate Handler instances(AuthenticationSuccessHandler, AuthenticationFailureHandler,..) to these classes, which makes the filters more configurable/reusable. A similar story for LogoutFilter(determineRedirectUrl removed in favour of adding a LogoutSuccessHandler).</p>
<h1>5. Changing the security related applicationContext files</h1>
<p>Last but not least, the spring applicationContext in which all the spring security beans are defined needs to change to.</p>
<p>At the very least, the spring security namespace schema will need to be changed from <a href="http://www.springframework.org/schema/security/spring-security-2.0.xsd">http://www.springframework.org/schema/security/spring-security-2.0.xsd</a> to <a href="http://www.springframework.org/schema/security/spring-security-3.0.xsd">http://www.springframework.org/schema/security/spring-security-3.0.xsd</a>. Forgetting to do this whill result in a BeanDefinitionParsingException upon application deploy.</p>
<p>The spring security xml namespace underwent quite some changes too.<br />
One of the most notable changes is the removal of custom-authentication-provider and custom-filter inside authenticationProvider and custom spring security filter beans. Authentication providers are now injected explicitly or configured as &lt;security:authentication-provider ref=””&gt; within the authenticationManager bean. Custom filters can be declared explicitly in the spring security filter chain bean declaration, or they can be included with &lt;custom-filter&gt; within the &lt;http&gt; element in case this one is used.<br />
Another notable change within the spring security xml namespace is that authentication-provider is now declared as a child of authentication-manager.</p>
<p>It is also necessary to change the classnames of the spring security framework classes that changed package in the applicationContext file(s).</p>
<p>And finally, some beans expect other properties than they used to. These injections need to be corrected too. For example, FilterSecurityInterceptor does not take a String anymore but needs to be injected with or a proper securityMetadataSource or needs to be declared with a fully configured security:filter-security-metadata-source tag.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.integratingstuff.com/2011/04/30/migrating-from-spring-security-2-to-spring-security-3/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

