Wednesday, April 27, 2011

Intalio : how to dynamically set task metadata

In this post, I'll explain how you can dynamically set values to the task metadata. By doing so, you'll be able to have absolute control of your tasks on runtime : dynamically assigning a task to one/several users, one/several user groups, defining a deadline, etc, would be a piece of cake.

To illustrate my point, let's define a real simple and basic process (its design shouldn't be a problem for you at all but if you're not familiar with the Intalio designer yet, just leave a comment and I'll send the whole sample project to you) :

1° Sample process


Two important things here :
  • for this post purpose, I didn't waste my time creating an elaborate AjaxForm for the task. Actually, I've simply created an empty form. So, don't waste yours neither if you're only trying to replicate the process.
  • as you can see on the screenshot, there's a warning mark on the form. The reason is that I did not assign the task to any user or group, given I am going to show how you can dynamically assign a task. For those who absolutely can't bear to have warnings in their project, just go ahead and assign a static user/group, the result will be the same.

2° Setting value on task metadata

Task metadata can have a value dynamically set through the mapper : set the focus on the node that is creating the task (in the example process, that is the red surrounded node) and open the Mapper view :


Then, expand the "createTaskMsg" root element and you'll see all the task metadata to which you can dynamically set a value. In this example, I've assigned the task to 2 users : examples/ewilliams and examples/msmith.

Note that in my example, though the users are set through the mapper, it is rather static because I used a static value. But this can be done dynamically because the value that is set to the userOwners element can be retrieved from a web service, a DB datasource, a message that is defined with an XSD, ...



3° Running the example

Executing this process will yield the following result in the UI-FW console :

Logged in as "intalio/admin":



Logged in as "examples/msmith":



Logged in as "examples/ewilliams":


By the way, in this example, you've probably noticed how convenient it is to have a column in the task list showing the users to whom a task has been assigned ... This feature could be very useful once you need to implement hierarchies among users and user groups.


4° Adding an actual dynamic metadata


Now that we've seen how values can be set to task metadata, let's add an actual dynamic metadata. In the process view, we'll map the String element that comes as input to the process when it is executed to the task deadline element : 



Now, let's start the same process from the BPMS console and as input, we'll provide the following value : 2011-12-31T11:00:00 (correct format for datetime type is yyyy-MM-ddThh:mm:ss)



As a result, the "examples/ewilliams" and "examples/msmith" users will have a task that must be completed by 11:00 December 31 2011.





Monday, April 25, 2011

Intalio : How to customize the task list in UI-FW console

In this post, I'll explain how the UI-FW console can be customized, that is, how you can add columns to the task list. Note that the additional columns are intended to display task metadata (such a task's status, owner user, owner group, ...). I've never needed to customize the task list to display information other than task related ones, so far, but it's definitely something I'll try to do later.

As an example, I'll add a column to display the user to whom a task is assigned.
Adding a column to the task list is done with the two following steps

1° Adding the new column to grids.jsp

The grids.jsp file is located in INTALIO_HOME\webapps\ui-fw\script
Edit this file and search for "Table for activity tasks" (should be at the line 546). Below this comment, you'll find the definition of the existing columns :

/*
    Table for activity tasks
    */
 var taskIcons = getToolbarIconsCodes(taskIconSet);
    var t1 = $("#table1").flexigrid($.extend({
      <% if(useToolbar) {%> 
        buttons : taskIcons,
        <%} %>
        params: [
        { name : 'type', value : 'PATask' },
        { name : 'update', value : true }
        ],
        colModel : [
        {
          display: '', 
          name : '_description', 
          width : width*0.44, 
          sortable : true, 
          align: 'left'},
        {
          display: '', 
          name : '_state', 
          width : width*0.035, 
          resize : true, 
          sortable : true, 
          align: 'center'},
        {
          display: '', 
          name : '_creationDate', 
          width : width*0.15, 
          sortable : true, 
          align: 'left'},
        {
          display: '', 
          name : '_deadline', 
          width : width*0.15, 
          sortable : true, 
          align: 'left'},
        {
          display: '', 
          name : '_priority', 
          width : width*0.070, 
          sortable : true, 
          align: 'center'},
        {
          display: '', 
          name : '_attachments', 
          width : width*0.12, 
          sortable : false, 
          align: 'center'}
        ]
    },p));

Then, simply add a new column definition block (between brackets) for a column named _userOwner and watch out for the comma (the different column definition blocks are separated by a comma) :
/*
    Table for activity tasks
    */
 var taskIcons = getToolbarIconsCodes(taskIconSet);
    var t1 = $("#table1").flexigrid($.extend({
      <% if(useToolbar) {%> 
        buttons : taskIcons,
        <%} %>
        params: [
        { name : 'type', value : 'PATask' },
        { name : 'update', value : true }
        ],
        colModel : [
        {
          display: '', 
          name : '_description', 
          width : width*0.44, 
          sortable : true, 
          align: 'left'},
        {
          display: '', 
          name : '_state', 
          width : width*0.035, 
          resize : true, 
          sortable : true, 
          align: 'center'},
        {
          display: '', 
          name : '_creationDate', 
          width : width*0.15, 
          sortable : true, 
          align: 'left'},
        {
          display: '', 
          name : '_deadline', 
          width : width*0.15, 
          sortable : true, 
          align: 'left'},
        {
          display: '', 
          name : '_priority', 
          width : width*0.070, 
          sortable : true, 
          align: 'center'},
        {
          display: '', 
          name : '_attachments', 
          width : width*0.12, 
          sortable : false, 
          align: 'center'},
        {
          display: '', 
          name : '_userOwner', 
          width : width*0.035,
          sortable : true, 
          align: 'left'}  
        ]
    },p));

Note that the value of the "key" attribute is used to define which label will be displayed in the column header. These labels can be specified in the internationalization files (properties files) located the INTALIO_HOME\webapps\ui-fw\WEB-INF\classes directory.

2° Editing the updates.jsp file

The updates.jsp file is located in INTALIO_HOME\webapps\ui-fw\WEB-INF\jsp.
This file needs to be edited in order to map the task metadata to the columns from the task list. In the updates.jsp file, there is a "choose" tag that contains a "Notification" section, a "PIPATask" section and an "otherwise" section. Simply add a "cell" definition in that last one and you good to go :


<cell>
     <c:choose>
      <c:when test="${taskHolder.task.userOwners != ''}">
                  ${taskHolder.task.userOwners}
      </c:when>
      <c:otherwise>none</c:otherwise>
      </c:choose>
</cell>

To correctly map a task metadata with a column, you'll need to indicate the correct attribute name from the Task.java class (in my case, taking a peek to the source was very helpful to find out what are the attributes name). For instance, if you use taskHolder.task.userOwnersWrong instead of taskHolder.task.userOwners, you'll get exceptions in the Intalio console and logs when loading the task list.
Here's the result :

 Thanks


Finally, I just want to thank Mr. Ihab El Alami (process expert at Intalio) who gave me some hints to customize the UI-FW console.

Sunday, April 24, 2011

Recipe to build the Intalio Tempo project (Windows environment)


For those who want or need to build the Intalio Tempo project from its source, I am pretty sure you ended up reading the install instruction provided in the GIT repository from the Tempo project (if not, here's the link to it : https://github.com/intalio/tempo/blob/master/INSTALL.txt).

When I followed these instructions myself (FYI, I was trying to build the project in a Windows XP environment), I faced lots of problem (gem version problems, missing Ruby environment variables and so on) and at some point, I had the feeling that the instructions of the INSTALL.txt file were mainly UNIX/Linux oriented.

It took me a few days to figure out how to build the project in my Windows XP and here's the steps to go through : 

1° Prerequisites

First and foremost, make sure you've installed JDK 5 and set the JAVA_HOME variable.


2° Retrieving the Tempo project's source

The Tempo project source are located in a GIT repository. Personally, I've installed the Windows installer for te 1.7.4 preview version (http://code.google.com/p/msysgit/downloads/detail?name=Git-1.7.4-preview20110204.exe&can=2&q=).

- Create a directory in C:/ and name it "Tempo-src"
- Open a GIT bash console and go into the "C:/Tempo-src" directory
- Execute the following command :
 
git clone git://github.com/intalio/tempo.git
 

3° Installing Ruby for Windows

Download and install Ruby 1.8.7 for Windows (available at : http://www.ruby-lang.org/en/downloads/).



4° Installing development kit

- Download and install the development kit for Ruby. This will allow your gems, among other things, to execute C and C++ libraries. This kit is available at http://rubyinstaller.org/downloads/

Actually, the executable that you've just downloaded is a 7-zip archive. Run it, specify the extraction path to C:\devKit

(If you specify another path, make sure it does not contain any blank !!! That will prevent you from getting some errors that are far from being well documented)

- Open a CMD shell, go into the C:\devKit directory and execute the following commands :

          - ruby dk.rb init
     - ruby dk.rb install

5° Installing buildr gem
- Open a CMD shell and execute the command : gem install buildr -v 1.3.5 --platform=mswin32
This command will install the version 1.3.5 of buildr for Windows platform. During the installation process, you may get some error concerning the documentation file installation. Hopefully, these files are not necessary for the following steps.


6° Installing hpricot gem

In order to build the Tempo project, you'll also need the hpricot gem. In a CMD shell, simply execute the command gem install hpricot --platform=mswin32


Patching buildr

It seems that there are some version compatibility issues between the different tools. These issues are resolved by applying a patch on configuration file. For more information, you can refer to this link : https://fedorahosted.org/candlepin/wiki/PatchingBuildr

The link above described an automated procedure to apply the patch but it only works for UNIX/Linux platform. Working on Windows, I've simply edited the concerned configuration file manually. To make things simpler, just go into the C:\Ruby187\lib\ruby\gems\1.8\gems\buildr-1.3.3\lib\buildr\java directory. Open the file named version_requirement.rb and replace its content by the following lines :


# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with this
# work for additional information regarding copyright ownership.  The ASF
# licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
# License for the specific language governing permissions and limitations under
# the License.


module Buildr
  
  #
  # See ArtifactNamespace#need
  class VersionRequirement
    
    CMP_PROCS = Gem::Requirement::OPS.dup
    CMP_REGEX = if defined?(Gem::Requirement::OP_RE)
  Gem::Requirement::OP_RE
 else
  Gem::Requirement::OPS.keys.map { |k| Regexp.quote k }.join "|"
 end

    CMP_CHARS = CMP_PROCS.keys.join
    BOOL_CHARS = '\|\&\!'
    VER_CHARS = '\w\.\-'

    class << self
      # is +str+ a version string?
      def version?(str)
        /^\s*[#{VER_CHARS}]+\s*$/ === str
      end
      
      # is +str+ a version requirement?
      def requirement?(str)
        /[#{BOOL_CHARS}#{CMP_CHARS}\(\)]/ === str
      end
      
      # :call-seq:
      #    VersionRequirement.create(" >1 <2 !(1.5) ") -> requirement
      #
      # parse the +str+ requirement 
      def create(str)
        instance_eval normalize(str)
      rescue StandardError => e
        raise "Failed to parse #{str.inspect} due to: #{e}"
      end

      private
      def requirement(req)
        unless req =~ /^\s*(#{CMP_REGEX})?\s*([#{VER_CHARS}]+)\s*$/
          raise "Invalid requirement string: #{req}"
        end
        comparator, version = $1, $2
        version = if defined?(Gem::Version.version)
   Gem::Version.new(0).tap { |v| v.version = version }
        else
   Gem::Version.new(version)
        end

        VersionRequirement.new(nil, [$1, version])
      end

      def negate(vreq)
        vreq.negative = !vreq.negative
        vreq
      end
      
      def normalize(str)
        str = str.strip
        if str[/[^\s\(\)#{BOOL_CHARS + VER_CHARS + CMP_CHARS}]/]
          raise "version string #{str.inspect} contains invalid characters"
        end
        str.gsub!(/\s+(and|\&\&)\s+/, ' & ')
        str.gsub!(/\s+(or|\|\|)\s+/, ' | ')
        str.gsub!(/(^|\s*)not\s+/, ' ! ')
        pattern = /(#{CMP_REGEX})?\s*[#{VER_CHARS}]+/
        left_pattern = /[#{VER_CHARS}\)]$/
        right_pattern = /^(#{pattern}|\()/
        str = str.split.inject([]) do |ary, i|
          ary << '&' if ary.last =~ left_pattern  && i =~ right_pattern
          ary << i
        end
        str = str.join(' ')
        str.gsub!(/!([^=])?/, ' negate \1')
        str.gsub!(pattern) do |expr|
          case expr.strip
          when 'not', 'negate' then 'negate '
          else 'requirement("' + expr + '")'
          end
        end
        str.gsub!(/negate\s+\(/, 'negate(')
        str
      end
    end

    def initialize(op, *requirements) #:nodoc:
      @op, @requirements = op, requirements
    end

    # Is this object a composed requirement?
    #   VersionRequirement.create('1').composed? -> false
    #   VersionRequirement.create('1 | 2').composed? -> true
    #   VersionRequirement.create('1 & 2').composed? -> true
    def composed?
      requirements.size > 1
    end

    # Return the last requirement on this object having an = operator.
    def default
      default = nil
      requirements.reverse.find do |r|
        if Array === r
          if !negative && (r.first.nil? || r.first.include?('='))
            default = r.last.to_s
          end
        else
          default = r.default
        end
      end
      default
    end

    # Test if this requirement can be satisfied by +version+
    def satisfied_by?(version)
      return false unless version
      unless version.kind_of?(Gem::Version)
        raise "Invalid version: #{version.inspect}" unless self.class.version?(version)
        version = Gem::Version.new(0).tap { |v| v.version = version.strip }
      end
      message = op == :| ? :any? : :all?
      result = requirements.send message do |req|
        if Array === req
          cmp, rv = *req
          CMP_PROCS[cmp || '='].call(version, rv)
        else
          req.satisfied_by?(version)
        end
      end
      negative ? !result : result
    end

    # Either modify the current requirement (if it's already an or operation)
    # or create a new requirement
    def |(other)
      operation(:|, other)
    end

    # Either modify the current requirement (if it's already an and operation)
    # or create a new requirement
    def &(other)
      operation(:&, other)
    end
    
    # return the parsed expression
    def to_s
      str = requirements.map(&:to_s).join(" " + @op.to_s + " ").to_s
      str = "( " + str + " )" if negative || requirements.size > 1
      str = "!" + str if negative
      str
    end

    attr_accessor :negative
    protected
    attr_reader :requirements, :op
    def operation(op, other)
      @op ||= op 
      if negative == other.negative && @op == op && other.requirements.size == 1
        @requirements << other.requirements.first
        self
      else
        self.class.new(op, self, other)
      end
    end
  end # VersionRequirement
end 

8° Building the Tempo project


And finally, in a CMD shell, execute the ruby command on the tempo-builder.rb file which is located in the C:\Tempo-src\tempo\rsc\scripts> directory :




At this point, if you got a message similar to "looking for TOOL_XXX (Axis2, Tomcat,and so on) and all I got was 404!", it is likely that the source of the Tempo project you've checked out are configured to work with previous version of the concerned tool.


So, you'll need to manually edit following file : C:\Tempo-src\tempo\rsc\scripts\lib\build_support.rb

To resolve this kind of issue, you could either change the version of change the path to the indicated resource. But my opinion is that it is safer to change the path and keep the same version. That way, you won't create new version incompatibilities.



9° External links


This recipe worked for me but if you still encounter errors following it, take a look at the Tempo development
group page (http://groups.google.com/group/tempo-dev) where you will definitely find some helpful information. Here's another helpful link that will provide you plenty of information about Intalio Tempo : http://intaliotempo.wordpress.com/.

 




Sunday, April 3, 2011

The importance of overriding Object's hashCode( ) method

During a recent conversation with some colleagues of mine, I figured out that some of them seem not to know what is the actual importance and utility of overriding the hashCode( ) method from the Object class.
Therefore, I wanted to dedicate a post for this topic.

1. Where is it used and how
The main purpose of the hashing algorithm is to improve performance of object retrieval from a hash collection such as a HashSet, HashMap and so on. Basically, such a collection can be seen as a series of buckets in which, objects will be stored. Each of these buckets is implicitly identified through the hashcode. Here's, for instance, how a HashSet can be seen : 
Fig. 1 - Empty HashSet

Every time an Object will be added to the HashSet, 2 operations will actually be done : 
  • The Object's HashCode ( ) method is executed in order to identify the bucket in which is will be stored
  • Since we are working with a Set, after having identify the appropriate bucket, the Object's equals(Object obj) method will be executed against each objects that are already stored in the same bucket to make sure a same object is not added twice to the Set.
Fig. 2 - Adding an Object to the HashSet

 2. hashCode( ) method's common issues

If the hashCode( ) method is not appropriately implemented or implemented at all : 
  • Several Objects that should be considered identical can be redundantly stored in the HashSet given they're not stored in the same bucket.
  • The contains(Object obj) method of the HashSet can return a wrong result because it was checking the wrong bucket
  • The remove(Object obj) method of the HashSet does not remove anything because, again, it was looking for the object to remove in the wrong bucket

3. Performance issues
Theoretically, an optimal hashCode( ) method would distribute the objects to store in a Hash collection among all the available buckets, so that the number of execution of the equals(Object obj) method is more or less the same regardless the bucket in which an object insertion/comparison/removal will be done

In order to check this by myself, I've written the following classes : 


package main;

public class Car {
 private String brand;
 private String model;
 
 public Car(String brand, String model){
  this.brand = brand;
  this.model = model;
 }
 
 public String getBrand() {
  return brand;
 }
 
 public void setBrand(String brand) {
  this.brand = brand;
 }
 
 public String getModel() {
  return model;
 }
 
 public void setModel(String model) {
  this.model = model;
 }

 @Override
 public boolean equals(Object obj) {  
  if (obj == null) {
   return false;
  }
  
  if (!(obj instanceof Car)) {
   return false;
  }
  
  Car other = (Car) obj;
  if (brand == null) {
   if (other.brand != null) {
    return false;
   }
  } else if (!brand.equalsIgnoreCase(other.brand)) {
   return false;
  }
  
  if (model == null) {
   if (other.model != null) {
    return false;
   }
  } else if (!model.equalsIgnoreCase(other.model)) {
   return false;
  }
  
  return true;
 }
 
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((brand == null) ? 0 : brand.hashCode());
  result = prime * result + ((model == null) ? 0 : model.hashCode());
  return result;
 }
}

package main;

import java.util.HashSet;
import java.util.Set;

public class HashMain {
 private static final int CATALOG_SIZE = 10000;
 
 public static void main(String[] args) {
  Set<Car> carCatalog = new HashSet<Car>();
  
  populateCarCatalog(carCatalog);
  emptyCarCatalog(carCatalog);
 }
 
 private static void populateCarCatalog(Set<Car> carCatalog){
  for(int i=1; i <= CATALOG_SIZE; i++){
   Car car = new Car("Brand ".concat(Integer.toString(i)), "Model ".concat(Integer.toString(i)));
   carCatalog.add(car);
  }
 }
 
 private static void emptyCarCatalog(Set<Car> carCatalog){
  long startTime = System.currentTimeMillis();
  
  for(int i=1; i <= CATALOG_SIZE; i++){
   Car car = new Car("Brand ".concat(Integer.toString(i)), "Model ".concat(Integer.toString(i)));
   carCatalog.remove(car);
  }
  
  System.out.println("Catalog emptied in : " + ((System.currentTimeMillis() - startTime) / 1000.0));
 }
} 

I had the Eclipse IDE generate the equals(Object obj) and hashCode( ) methods and the execution took around 0.01 second.

After making the hashCode( ) method constantly return the value 1, the execution took 2.681 sec.

The reason is that at the first removal, we had to make 10000 comparisons because all the Car objects were stored in the same bucket. At the second removal, 9999 comparisons. At the third removal, 9998. And so on ...

4. Conclusion
A good practice would be to always implement the equals(Object obj) and hashCode( ) methods when defining a POJO class, no matter it will be stored in a hash collection or not.  

Saturday, February 5, 2011

Communication between Java threads : exception propagation example

In this article, I'll show you how a child thread can report an exception to its parent thread.
This mechanism prove to be very useful and convenient when the parent thread needs to take some action whenever one of its child thread fails to execute to its completion.

In the following example, a TaskManager instance acts as the main thread and a Task instance, as its child thread. The TaskManager launches a Task and the latter will throw an Exception. If we want the main thread to be notified when an exception is thrown in its child thread, one solution is to have it implement the ExceptionListener interface and to have the child thread class get a reference to the instance of ExceptionListener. 

For those who have been working with Java Swing, SWT, ..., the principle is similar to adding a listener to a graphical component.

The following code excerpt is rather straightforward, so, I guess you won't need any further explanation. By the way, I called the sleep(...) method on both threads to simulate a more realistic behavior for the TaskManager as well as the Task.



package main;

import java.beans.ExceptionListener;
import java.io.IOException;

public class TaskManager implements ExceptionListener{
   
    @Override
    public void exceptionThrown(Exception e) {
        System.out.println("An error occured in subtask --> TaskManager may need to take some action");
    }
   
   
    public static void main(String[] args) {
        TaskManager tm = new TaskManager();
        Task t = new Task(tm);
       
        Thread th = new Thread(t);
        th.start();
       
        int cpt = 0;
       
        // Main thread doing its own work
        while(cpt++ < 10){
            try {
                Thread.sleep(1000);
                System.out.println("Thread : " + Thread.currentThread().getName() + " - doing its job");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }   
}

class Task implements Runnable{
    private ExceptionListener listener;
   
    public Task(ExceptionListener listener){
        this.listener = listener;
    }
   
    @Override
    public void run(){
        try{
            // ... simulating some processing done by the child Thread
            Thread.sleep(4000);
            throw new IOException("... the specified file does not exist (for instance)");
        }catch(IOException ioe){
            if(this.listener != null){
                // propagating the caught Exception to the ExceptionListener
                listener.exceptionThrown(ioe);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
   
}

Saturday, January 8, 2011

Intalio : how to deploy a process as an archive (without designer)

Introduction

Though deploying a process from the Intalio Designer is really straightforward (done in 1 click once the project is appropriately configured), one might need to be able to deploy a process without the Designer.

Step 1 : creating an archive from the process project

From the Explorer View in the Intalio Designer, click on the project you want to deploy to select it. Then, click on the button with the gear icon to open the process configuration page.


On the configuration page, check the "Archive the bundle" box : 
The "Archive the bundle" option specifies that a zip archive on the project will be created. The possible options are :
  • only deploy
  • only archive
  • deploy + archive
Once you're ready with the configuration, click on the "Archive button" :

The Designer console will then display the path of the generated archive :


As you can see, the project is archived in a zip file and it is named accordingly with your project name.

Step 2 : deploying the archive

 The content of the archive is as such :


Simply unzip the archive in a directory that has the same name as your project, in the ...\intalio-bpms-6.0.3.022\var\deploy directory.

Manually, you can simply copy the archive in the ...\intalio-bpms-6.0.3.022\var\deploy directory and unzip it with the option "Extrat to yourProjectName".

Note that the Intalio server supports hot deployment and consequently, you can unzip the archive with the server running as well as stopped.

Sunday, January 2, 2011

JBOSS AS 6 : Adding and testing a new Oracle 11g datasource

1 Prerequisites
  • Oracle 11g (R1 or R2 doesn't matter but anyway, I installed the R1)
  • a defined database
  • JBoss AS 6
 2 Adding the new datasource


As described in the JBoss AS documentation, we just need to create and configure a new RDBS-ds.xml file and put it in the jboss-6.0.0.Final\server\your-server-configuration\deploy directory.

In my case, in order to get to the point as quickly as possible (and because I only need a basic datasource configuration at the moment), I created my own oracle-ds.xml file by copying the sample one which is located at : jboss-6.0.0.Final\docs\examples\jca

Here's my own oracle-ds.xml file :

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
    <local-tx-datasource>
        <jndi-name>OracleDS</jndi-name>
        <connection-url>jdbc:oracle:thin:@localhost:1521:CFDB</connection-url>
        <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
        <user-name>CF_USER</user-name>
        <password>mypassword</password>
        <valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleValidConnectionChecker</valid-connection-checker-class-name>
        <metadata>
            <type-mapping>Oracle11g</type-mapping>
        </metadata>
    </local-tx-datasource>
</datasources>

At this point there's 2 things you should pay attention to :
  • Make sure there's no other xxx-ds.xml file with an identical jndi-name, otherwise, the datasource cannot be deployed correctly.
  • Don't forget to drop the appropriate JDBC driver library in the same directory as the datasource configuration file, that is : jboss-6.0.0.Final\server\your-server-configuration\deploy
3 Testing the connection
1° start the application server
2° log into the server administration console (http://localhost:xxxx/admin-console/login.seam)
3° go to the datasource section as shown below


4° Select the newly created datasource and open the CONTROL tab


5° Click on the Test connection button to run a test
6° As as result, make sure the test ran successfully and that a connection has been obtained


    To check whether a connection has been obtained, especially if you ran several tests, click on the Show 
    details below... button and make sure the result value is Yes


    The result value is critical to test the connection to your datasource because as long as your datasource 
    configuration file is correct, the test could run successfully while your database is not even running, for 
    instance ...