<?xml version="1.0" encoding="UTF-8"?>
<!-- generator="lyceum/1.0.2" -->
<!-- This is 512 bytes of nonsense, since the Firefox 2 developers, in one of the strangest decisions ever, decidedthey would obsolete XML styles by overriding them without permission.  Furthermore, the developers appear to be disinterested in fixing this.  Therefore, we use the unofficial workaround, which includes fillingup the first 512 bytes of a document so that the sniffer doesn't encounter the RSS tag.  I really enjoyusing Firefox, but this particular behavior really annoys me! Anyway, since I'm almost at 512 characters, I'mgoing to ramble on for another minute in this comment, and then, without further adue, present you with avalid XML feed. -->
<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/"
	>

<channel>
	<title>Red Hat Magazine</title>
	<link>http://www.redhatmagazine.com</link>
	<description></description>
	<pubDate>Thu, 04 Sep 2008 22:09:36 +0000</pubDate>
	<generator>http://lyceum.ibiblio.org/?v=1.0.2</generator>
	<language>en</language>
			<item>
		<title>JBoss Drools how-to: Tuning Guvnor, part 2</title>
		<link>http://www.redhatmagazine.com/2008/08/14/jboss-drools-how-to-tuning-guvnor-part-2/</link>
		<comments>http://www.redhatmagazine.com/2008/08/14/jboss-drools-how-to-tuning-guvnor-part-2/#comments</comments>
		<pubDate>Thu, 14 Aug 2008 22:34:10 +0000</pubDate>
		<dc:creator>Jaroslaw Kijanowski</dc:creator>
		
		<category>technical</category>

		<category>JBoss</category>

		<guid isPermaLink="false">http://www.redhatmagazine.com/2008/08/14/jboss-drools-how-to-tuning-guvnor-part-2/</guid>
		<description><![CDATA[Guvnor is the business rules management system in Drools 5. When you deploy it out of the box, you get an unsecured web application that stores data in Jackrabbit&#8217;s embedded Derby database. 
This two-part article explains how to tune Guvnor deployed on JBoss Application Server 4.2.3. (If you missed the first half of the series, [...]]]></description>
			<content:encoded><![CDATA[<p>Guvnor is the business rules management system in Drools 5. When you deploy it out of the box, you get an unsecured web application that stores data in Jackrabbit&#8217;s embedded Derby database. </p>
<p>This two-part article explains how to tune Guvnor deployed on JBoss Application Server 4.2.3. (If you missed the first half of the series, <a href="http://www.redhatmagazine.com/2008/08/12/jboss-drools-how-to-tuning-guvnor-part-1/">catch up</a> in our archives.)This means that we will use the container&#8217;s configuration files and security infrastructure. This installment covers enabling password validation based on an OpenLDAP server, moving from the default data repository, and enabling SSL for better security.<a id="more-1021"></a></p>
<ol>
<li><a href="#ldap">Use OpenLDAP as a user repository</a></li>
<li><a href="#mysql">Use MySQL as a data repository</a></li>
<li><a href="#ssl">Enable SSL</a></li>
<li><a href="#secured">How to use a secured Guvnor package</a></li>
<li><a href="#summary">Summary</a></li>
</ol>
<p><a name="ldap"></a></p>
<h2>Use OpenLDAP as a user repository</h2>
<p>There are several reasons why you would want to use an LDAP directory instead of a clear text file &#8212; security, provisioning,  and reuseability are just a few. First of all, we need a directory. OpenLDAP will do for this example. Download and extract the bits from the OpenLDAP <a href="http://www.openldap.org/">home page</a>. In this example, I&#8217;ve used <a href="ftp://ftp.openldap.org/pub/OpenLDAP/openldap-stable/openldap-stable-20071118.tgz">openldap-2.3.39.tgz</a>. Next, go to the directory where you&#8217;ve extracted the installation files and perform following steps:</p>
<pre>
$ mkdir -p /data/openldap-2.3.39
$ ./configure --prefix=/data/openldap-2.3.39
$ make depend
$ make
$ make install
</pre>
<p>For more detailed instructions, look at the INSTALL file or the <a href="http://www.openldap.org/doc/admin23/">OpenLDAP Administrator&#8217;s Guide</a>.</p>
<p>The next instructions will configure our directory and create a tree which looks like this:</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2744228157/" title="Fig 2.1  tree by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3093/2744228157_6ccd75dac5.jpg" width="500" height="334" alt="Fig 2.1  tree" /></a></p>
<div class="caption">Fig 2.1 tree</div>
<p>We need to initialize the LDAP server and provide data like the root suffix, directory manager, and password. We also want to enable SSL like so:</p>
<pre>
$ mkdir /data/openldap-2.3.39/ssl
$ openssl req -newkey rsa:1024 -x509 -nodes -out /data/openldap-2.3.39/ssl/server.pem -keyout /data/openldap-2.3.39/ssl/server.pem -days 365
Generating a 1024 bit RSA private key
....++++++
................++++++
writing new private key to 'server.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [GB]:EU
State or Province Name (full name) [Berkshire]:Mazovia
Locality Name (eg, city) [Newbury]:Warsaw
Organization Name (eg, company) [My Company Ltd]:Kijanowski
Organizational Unit Name (eg, section) []:Guvnor
Common Name (eg, your name or your server's hostname) []:localhost
Email Address []:a@a.a
</pre>
<p>Since this is a self-signed certificate we will need to add it to the client&#8217;s (JBoss AS) truststore.</p>
<pre>
$ openssl
OpenSSL> x509 -inform PEM -outform DER -in /data/openldap-2.3.39/ssl/server.pem -out /data/openldap-2.3.39/ssl/server.der
OpenSSL> exit

$ keytool -import -file /data/openldap-2.3.39/ssl/server.der -keystore $JBOSS_SERVER/conf/ldap.truststore

Enter keystore password:  qwerty
Owner: EMAILADDRESS=a@a.a, CN=localhost, OU=Guvnor, O=Kijanowski, L=Warsaw, ST=Mazovia, C=EU
Issuer: EMAILADDRESS=a@a.a, CN=localhost, OU=Guvnor, O=Kijanowski, L=Warsaw, ST=Mazovia, C=EU
Serial number: d8537a079c5eed59
Valid from: Wed Jul 16 18:35:50 CEST 2008 until: Thu Jul 16 18:35:50 CEST 2009
Certificate fingerprints:
         MD5:  25:C5:88:7B:D4:88:02:46:F1:EF:0D:6B:D6:EE:1F:A7
         SHA1: 57:B8:F4:25:77:F0:12:BD:B2:2E:DD:7D:CE:09:D2:D4:96:56:BC:26
Trust this certificate? [no]:  yes
Certificate was added to keystore
</pre>
<p>To enable the new truststore, edit <code>$JBOSS_SERVER/deploy/properties-service.xml</code> and add following lines:</p>
<pre>
&lt;attribute name="Properties"&gt;
    javax.net.ssl.trustStore=/data/jboss-4.2.3.GA/server/<chosen_config>/conf/ldap.truststore
    javax.net.ssl.trustStorePassword=qwerty
&lt;/attribute&gt;
</pre>
<p>Now edit the file <code>/data/openldap-2.3.39/etc/openldap/slapd.conf</code>:</p>
<pre>
include         /data/openldap-2.3.39/etc/openldap/schema/core.schema
include         /data/openldap-2.3.39/etc/openldap/schema/cosine.schema
include         /data/openldap-2.3.39/etc/openldap/schema/inetorgperson.schema

pidfile         /data/openldap-2.3.39/var/run/slapd.pid
argsfile        /data/openldap-2.3.39/var/run/slapd.args

database        bdb
suffix          "dc=kijanowski,dc=eu"
rootdn          "cn=DirManager,dc=kijanowski,dc=eu"
rootpw          secret
directory       /data/openldap-2.3.39/var/openldap-data
index   objectClass     eq

TLSCipherSuite HIGH:MEDIUM:-SSLv2
TLSCACertificateFile /data/openldap-2.3.39/ssl/server.pem
TLSCertificateFile /data/openldap-2.3.39/ssl/server.pem
TLSCertificateKeyFile /data/openldap-2.3.39/ssl/server.pem
TLSVerifyClient never
</pre>
<p>The rootpw attribute should be changed from &#8217;secret&#8217; to:</p>
<pre>
$ /data/openldap-2.3.39/sbin/slappasswd -s admin123
</pre>
<p>where &#8216;admin123&#8242; is the new directory manager&#8217;s password. For better performance, you can create a config file for the backend database or copy the sample configuration file like so:</p>
<pre>
$ cp /data/openldap-2.3.39/var/openldap-data/DB_CONFIG.example /data/openldap-2.3.39/var/openldap-data/DB_CONFIG
</pre>
<p>To start the server with a customized listener, run:</p>
<pre>
$ /data/openldap-2.3.39/libexec/slapd -h ldaps://localhost:16636
</pre>
<p>You can make sure your LDAP server is up and running (listening) by running:</p>
<pre>
$ netstat -an|grep 16636
tcp        0      0 127.0.0.1:16636             0.0.0.0:*                   LISTEN
</pre>
<p>To create tree like the one shown above, we need to add the following myorg.ldif file:</p>
<pre>
dn: dc=kijanowski,dc=eu
objectclass: top
objectclass: dcObject
objectclass: organization
dc: kijanowski
o: kijanowski

dn: o=guvnor,dc=kijanowski,dc=eu
objectclass: top
objectclass: organization
o: guvnor

dn: ou=People,o=guvnor,dc=kijanowski,dc=eu
objectclass: top
objectclass: organizationalUnit
ou: People

dn: uid=admin,ou=People,o=guvnor,dc=kijanowski,dc=eu
objectclass: top
objectclass: uidObject
objectclass: person
objectClass: inetOrgPerson
uid: admin
cn: Guvnor Admin
sn: Administrator
userPassword: {SSHA}ZGUjbzh0wN0JoWxIAcZfFXpV5MIu/gZw

dn: uid=user1,ou=People,o=guvnor,dc=kijanowski,dc=eu
objectclass: top
objectclass: uidObject
objectclass: person
objectClass: inetOrgPerson
uid: user1
cn: Regular User
sn: Regular
userPassword: {SSHA}Gcif1SlGPu2vHrtoLGYlKXbKBytJiVVF

dn: ou=Roles,o=guvnor,dc=kijanowski,dc=eu
objectClass: top
objectClass: organizationalUnit
ou: Roles

dn: cn=admin,ou=Roles,o=guvnor,dc=kijanowski,dc=eu
objectClass: top
objectClass: groupOfNames
cn: admin
description: the GuvnorAdmin group
member: uid=admin,ou=People,o=guvnor,dc=kijanowski,dc=eu

dn: cn=regular,ou=Roles,o=guvnor,dc=kijanowski,dc=eu
objectClass: top
objectClass: groupOfNames
cn: regular
description: the Guvnor Regular group
member: uid=user1,ou=People,o=guvnor,dc=kijanowski,dc=eu
</pre>
<p>The passwords for admin and user1 are &#8216;9uvn04&#8242; and &#8216;user1&#8242; (respectively) and were generated with slappasswd. To add this ldif to our directory, we will use an ldap client application called ldapadd. First we need to update its configuration to be able to talk over SSL. Edit the file <code>/data/openldap-2.3.39/etc/openldap/ldap.conf</code> and add following line:</p>
<pre>
TLS_REQCERT allow
</pre>
<p>This will prevent us from getting errors like these:</p>
<pre>
client side:
ldap_initialize( ldaps://localhost:16636 )
ldap_bind: Can't contact LDAP server (-1)
        additional info: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

server side:
TLS trace: SSL3 alert read:fatal:unknown CA
TLS trace: SSL_accept:failed in SSLv3 read client certificate A
TLS: can't accept.
TLS: error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca s3_pkt.c:1057
connection_read(11): TLS accept failure error=-1 id=4, closing
connection_closing: readying conn=4 sd=11 for close
connection_close: conn=4 sd=11
</pre>
<p>Now we can add the ldif file to our directory:</p>
<pre>
$ /data/openldap-2.3.39/bin/ldapadd -x -D "cn=DirManager,dc=kijanowski,dc=eu" -H ldaps://localhost:16636 -w admin123 -f myorg.ldif

adding new entry "dc=kijanowski,dc=eu"
adding new entry "o=guvnor,dc=kijanowski,dc=eu"
adding new entry "ou=People,o=guvnor,dc=kijanowski,dc=eu"
adding new entry "uid=admin,ou=People,o=guvnor,dc=kijanowski,dc=eu"
adding new entry "uid=user1,ou=People,o=guvnor,dc=kijanowski,dc=eu"
adding new entry "ou=Roles,o=guvnor,dc=kijanowski,dc=eu"
adding new entry "cn=admin,ou=Roles,o=guvnor,dc=kijanowski,dc=eu"
adding new entry "cn=regular,ou=Roles,o=guvnor,dc=kijanowski,dc=eu"
</pre>
<p>The last step is to configure JAAS in <code>$JBOSS_SERVER/conf/login-config.xml</code>. Replace the previous file based login module with this one:</p>
<pre>
&lt;application-policy name="guvnor"&gt;
    &lt;authentication&gt;
        &lt;login-module code="org.jboss.security.auth.spi.LdapExtLoginModule" flag="required" &gt;
            &lt;module-option name="java.naming.provider.url"&gt;ldaps://localhost:16636&lt;/module-option&gt;
            &lt;module-option name="java.naming.security.protocol"&gt;ssl&lt;/module-option&gt;
            &lt;module-option name="bindDN"&gt;cn=DirManager,dc=kijanowski,dc=eu&lt;/module-option&gt;
            &lt;module-option name="bindCredential"&gt;admin123&lt;/module-option&gt;

            &lt;module-option name="baseCtxDN"&gt;ou=People,o=guvnor,dc=kijanowski,dc=eu&lt;/module-option&gt;
            &lt;module-option name="baseFilter"&gt;(uid={0})&lt;/module-option&gt;

            &lt;module-option name="rolesCtxDN"&gt;ou=Roles,o=guvnor,dc=kijanowski,dc=eu&lt;/module-option&gt;
            &lt;module-option name="roleFilter"&gt;(member={1})&lt;/module-option&gt;
            &lt;module-option name="roleAttributeID"&gt;cn&lt;/module-option&gt;

            &lt;module-option name="roleRecursion"&gt;-1&lt;/module-option&gt;
            &lt;module-option name="searchScope"&gt;ONELEVEL_SCOPE&lt;/module-option&gt;
        &lt;/login-module&gt;
    &lt;/authentication&gt;
&lt;/application-policy&gt;
</pre>
<p>Now restart JBoss AS and try to login as admin with password 9uvn04. The application server will talk with the OpenLDAP server over SSL. If you want to shutdown the OpenLDAP server you need to determine its PID and interrupt it by sending the process a SIGINT signal:</p>
<pre>
$ kill -INT `cat /data/openldap-2.3.39/var/run/slapd.pid`
</pre>
<p><a name="mysql"></a></p>
<h2>Use MySQL as a data repository</h2>
<p>Jackrabbit has been chosen as a Java Content Repository (JCR) implementation. By default, it uses the Derby database as a backend. You may want to switch to a database you are more familiar with, and can regularly back up and properly tune. If you have already used the file-based repository and don&#8217;t want to loose all your assets, export them. After MySQL is up and running, import them back. To export your current repository, go to the &#8216;Administration&#8217; menu on the left side, expand &#8216;Admin,&#8217; choose &#8216;Import/Export,&#8217; and click on &#8216;Export&#8217;:</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2745062730/" title="Fig 2.2  export by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3255/2745062730_9186be390d.jpg" width="500" height="188" alt="Fig 2.2  export" /></a></p>
<div class="caption">Fig 2.2 export</div>
<p>Shut down the server and set up MySQL as your new repository. First, download <a href="http://dev.mysql.com/">MySQL</a> and extract it. I will use the <a href="http://dev.mysql.com/get/Downloads/MySQL-5.0/mysql-5.0.51a-linux-i686.tar.gz/from/http://mysql.linux.cz/">community server 5.0.51a standard</a>,  extracted to /data/mysql-5.0.51. As root, perform the following steps. (For more details, have a look at the INSTALL-BINARY file):</p>
<pre>
$ /usr/sbin/groupadd mysql5
$ /usr/sbin/useradd -g mysql5 mysql5
$ cd /data/mysql-5.0.51
$ chown -R mysql5 .
$ chgrp -R mysql5 .
$ /data/mysql-5.0.51/scripts/mysql_install_db --user=mysql5

$ chown -R root .
$ chown -R mysql5 data

# now start MySQL
$ /data/mysql-5.0.51/bin/mysqld_safe --user=mysql5 &#038;

# and create a password for root
$ /data/mysql-5.0.51/bin/mysqladmin -u root password mysqladminpwd
To shutdown the MySQL server run:
$ /data/mysql-5.0.51/bin/mysqladmin -u root shutdown -p
Logout as root and log in to MySQL to create a user and database for Guvnor:
$ /data/mysql-5.0.51/bin/mysql -u root -p
mysql&gt; create database guvnor;
Query OK, 1 row affected (0.00 sec)

mysql&gt; grant all privileges on guvnor.* to 'guvnor-user'@'localhost' identified by 'guvnor-pwd';
Query OK, 0 rows affected (0.00 sec)

mysql&gt; flush privileges;
Query OK, 0 rows affected (0.00 sec)
</pre>
<p>Now the DB side is complete.</p>
<p>Edit the <code>$GUVNOR/WEB-INF/components.xml</code> file and provide a path to where you would like to keep the repository configuration files. You can leave the default value&#8211;which is the JBoss Application Server&#8217;s bin directory&#8211;however it is recommended to provide a location that is regularly backed up. Under the &#8216;repositoryConfiguration&#8217; component add:</p>
<pre>
&lt;property name="homeDirectory"&gt;/data/GuvnorRepo/&lt;/property&gt;
</pre>
<p>This last step creates a repository.xml file. It is created by default when running Guvnor the first time and is placed into the AS bin directory. This file configures the data repository. We would like to use MySQL, so we will create <code>/data/GuvnorRepo/repository.xml</code>:</p>
<pre>
&lt;?xml version="1.0"?&gt;
&lt;!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.4//EN"
                            "http://jackrabbit.apache.org/dtd/repository-1.4.dtd"&gt;
&lt;Repository&gt;

        &lt;!-- Define where to store global data --&gt;
        &lt;FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem"&gt;
                &lt;param name="driver" value="com.mysql.jdbc.Driver"/&gt;
                &lt;param name="url" value="jdbc:mysql://localhost:3306/guvnor" /&gt;
                &lt;param name="user" value="guvnor-user" /&gt;
                &lt;param name="password" value="guvnor-pwd" /&gt;
                &lt;param name="schema" value="mysql"/&gt;
                &lt;param name="schemaObjectPrefix" value="Repository_FS_"/&gt;
       &lt;/FileSystem&gt;

    &lt;Security appName="Jackrabbit"&gt;
        &lt;AccessManager class="org.apache.jackrabbit.core.security.SimpleAccessManager"&gt;
        &lt;/AccessManager&gt;

        &lt;LoginModule class="org.apache.jackrabbit.core.security.SimpleLoginModule"&gt;
        &lt;/LoginModule&gt;
    &lt;/Security&gt;

    &lt;Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default"/&gt;

    &lt;Workspace name="${wsp.name}"&gt;

                &lt;PersistenceManager class="org.apache.jackrabbit.core.state.db.SimpleDbPersistenceManager"&gt;
                        &lt;param name="driver" value="com.mysql.jdbc.Driver"/&gt;
                        &lt;param name="url" value="jdbc:mysql://localhost:3306/guvnor" /&gt;
                        &lt;param name="user" value="guvnor-user" /&gt;
                       &lt;param name="password" value="guvnor-pwd" /&gt;
                        &lt;param name="schema" value="mysql"/&gt;
                        &lt;param name="schemaObjectPrefix" value="WS_PM_${wsp.name}_" /&gt;
                        &lt;!-- param name="externalBLOBs" value="false" /--&gt;
                &lt;/PersistenceManager&gt;

                &lt;FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem"&gt;
                        &lt;param name="driver" value="com.mysql.jdbc.Driver"/&gt;
                        &lt;param name="url" value="jdbc:mysql://localhost:3306/guvnor" /&gt;
                        &lt;param name="user" value="guvnor-user" /&gt;
                        &lt;param name="password" value="guvnor-pwd" /&gt;
                        &lt;param name="schema" value="mysql"/&gt;
                        &lt;param name="schemaObjectPrefix" value="WS_FS_${wsp.name}_"/&gt;
                &lt;/FileSystem&gt;

        &lt;!--
            Search index and the file system it uses.
            class: FQN of class implementing the QueryHandler interface
        --&gt;
        &lt;SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex"&gt;
            &lt;param name="path" value="${wsp.home}/index"/&gt;
            &lt;param name="textFilterClasses" value="org.apache.jackrabbit.extractor.MsWordTextExtractor,org.apache.jackrabbit.extractor.MsExcelTextExtractor,org.apache.jackrabbit.extractor.MsPowerPointTextExtractor,org.apache.jackrabbit.extractor.PdfTextExtractor,org.apache.jackrabbit.extractor.OpenOfficeTextExtractor,org.apache.jackrabbit.extractor.RTFTextExtractor,org.apache.jackrabbit.extractor.HTMLTextExtractor,org.apache.jackrabbit.extractor.XMLTextExtractor"/>
            &lt;param name="extractorPoolSize" value="2"/&gt;
            &lt;param name="supportHighlighting" value="true"/&gt;
        &lt;/SearchIndex&gt;
    &lt;/Workspace&gt;

    &lt;Versioning rootPath="${rep.home}/version"&gt;

        &lt;FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem"&gt;
                        &lt;param name="driver" value="com.mysql.jdbc.Driver"/&gt;
                        &lt;param name="url" value="jdbc:mysql://localhost:3306/guvnor" /&gt;
                        &lt;param name="user" value="guvnor-user" /&gt;
                        &lt;param name="password" value="guvnor-pwd" /&gt;
                        &lt;param name="schema" value="mysql"/&gt;
                        &lt;param name="schemaObjectPrefix" value="Versoning_FS_"/&gt;
                &lt;/FileSystem&gt;

                &lt;PersistenceManager class="org.apache.jackrabbit.core.state.db.SimpleDbPersistenceManager"&gt;
                        &lt;param name="driver" value="com.mysql.jdbc.Driver"/&gt;
                        &lt;param name="url" value="jdbc:mysql://localhost:3306/guvnor" /&gt;
                        &lt;param name="user" value="guvnor-user" /&gt;
                        &lt;param name="password" value="guvnor-pwd" /&gt;
                        &lt;param name="schema" value="mysql"/&gt;
                        &lt;param name="schemaObjectPrefix" value="Versioning_PM_" /&gt;
                        &lt;param name="externalBLOBs" value="false" /&gt;
                &lt;/PersistenceManager&gt;
    &lt;/Versioning&gt;

    &lt;SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex"&gt;
        &lt;param name="path" value="${rep.home}/repository/index"/&gt;
        &lt;param name="textFilterClasses" value="org.apache.jackrabbit.extractor.MsWordTextExtractor,org.apache.jackrabbit.extractor.MsExcelTextExtractor,org.apache.jackrabbit.extractor.MsPowerPointTextExtractor,org.apache.jackrabbit.extractor.PdfTextExtractor,org.apache.jackrabbit.extractor.OpenOfficeTextExtractor,org.apache.jackrabbit.extractor.RTFTextExtractor,org.apache.jackrabbit.extractor.HTMLTextExtractor,org.apache.jackrabbit.extractor.XMLTextExtractor"/>
       &lt;param name="extractorPoolSize" value="2"/&gt;
        &lt;param name="supportHighlighting" value="true"/&gt;
    &lt;/SearchIndex&gt;

&lt;/Repository&gt;
</pre>
<p>As you see we&#8217;re using the com.mysql.jdbc.Driver, so we need to provide it. Download the mysql java connector from <a href="http://dev.mysql.com/">MySQL</a>, unzip it, and copy the JAR file to $JBOSS_SERVER/lib. (I&#8217;m using <a href="http://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.6.zip/from/http://mysql.linux.cz/">mysql-connector-java-5.1.6</a>.) </p>
<p>Now you can start the app server. If you have exported your assets, just go to the &#8216;Administration&#8217; menu, expand &#8216;Admin,&#8217; choose &#8216;Import/Export,&#8217; and import your xml file. Please note that you have to unzip your exported xml file before you can upload it.</p>
<p><a name="ssl"></a></p>
<h2>Enable SSL</h2>
<p>The last tweak is enabling SSL. It not only provides security, but also ensures the transmitted data hasn&#8217;t been modified. This is strictly a server-side task. </p>
<p>First, we need a certificate. Please note that as the &#8220;first and last name&#8221; you have to provide the <strong>fully qualified domain name</strong> of the host. For testing purposes you can use localhost like so:</p>
<pre>
$ keytool -genkey -alias guvnor -keyalg RSA -keystore $JBOSS_SERVER/conf/guvnor.keystore -validity 365

Enter keystore password:  guvnorkspwd
What is your first and last name?
  [Unknown]:  localhost
What is the name of your organizational unit?
  [Unknown]:  My Department
What is the name of your organization?
  [Unknown]:  My Company
What is the name of your City or Locality?
  [Unknown]:  My City
What is the name of your State or Province?
  [Unknown]:  My State
What is the two-letter country code for this unit?
  [Unknown]:  US
Is CN=My Name, OU=My Department, O=My Company, L=My City, ST=My State, C=US correct?
  [no]:  yes

Enter key password for <guvnor>
        (RETURN if same as keystore password):
</pre>
<p>Now we can enable an SSL connector. Edit the file $JBOSS_SERVER/deploy/jboss-web.deployer/server.xml like so:</p>
<pre>
&lt;Connector port="8443" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS"
               keystoreFile="${jboss.server.home.dir}/conf/guvnor.keystore"
               keystorePass="guvnorkspwd"/&gt;
</pre>
<p>Restart your JBoss Application Server. Guvnor should be available at <a href="https://localhost:8443/drools-guvnor">https://localhost:8443/drools-guvnor</a>.</p>
<p><a name="secured"></a></p>
<h2>How to use a secured Guvnor package</h2>
<p>The last part of this article shows how you can access a drools package from Guvnor in a secure way. This is very straight forward if you use certificates signed by trusted authorities. In our test environment, it&#8217;s a little bit more complicated since we use self-signed certificates. </p>
<p>First create a package and deploy it. I&#8217;ll use the package we made during the quick introduction. This package is available under <a href="https://localhost:8443/drools-guvnor/org.drools.guvnor.Guvnor/package/myNewPackage/LATEST">https://localhost:8443/drools-guvnor/org.drools.guvnor.Guvnor/package/myNewPackage/LATEST</a>. Replace the url attribute with the new value in Guvnor.properties:</p>
<pre>
url=https://localhost:8443/drools-guvnor/org.drools.guvnor.Guvnor/package/myNewPackage/LATEST
</pre>
<p>One would expect that running the Drools application should end successfully, however this is not the case:</p>
<pre>
RuleAgent(default) INFO (Wed Jul 23 20:38:03 CEST 2008): Configuring with newInstance=false, secondsToRefresh=-1
RuleAgent(default) INFO (Wed Jul 23 20:38:03 CEST 2008): Configuring package provider : URLScanner monitoring URLs:  https://localhost:8443/drools-guvnor/org.drools.guvnor.Guvnor/package/myNewPackage/LATEST
RuleAgent(default) WARNING (Wed Jul 23 20:38:04 CEST 2008): Was an error contacting https://localhost:8443/drools-guvnor/org.drools.guvnor.Guvnor/package/myNewPackage/LATEST. Reponse header: {}
RuleAgent(default) EXCEPTION (Wed Jul 23 20:38:04 CEST 2008): Was unable to reach server.. Stack trace should follow.
java.io.IOException: Was unable to reach server.
        at org.drools.agent.URLScanner.hasChanged(URLScanner.java:149)
        at org.drools.agent.URLScanner.getChangeSet(URLScanner.java:113)
        at org.drools.agent.URLScanner.loadPackageChanges(URLScanner.java:90)
        at org.drools.agent.RuleAgent.checkForChanges(RuleAgent.java:341)
        at org.drools.agent.RuleAgent.refreshRuleBase(RuleAgent.java:300)
        at org.drools.agent.RuleAgent.configure(RuleAgent.java:285)
        at org.drools.agent.RuleAgent.init(RuleAgent.java:209)
        at org.drools.agent.RuleAgent.newRuleAgent(RuleAgent.java:177)
        at org.drools.agent.RuleAgent.newRuleAgent(RuleAgent.java:149)
        at org.drools.agent.RuleAgent.newRuleAgent(RuleAgent.java:217)
        at kijanowski.eu.GuvnorTest.main(GuvnorTest.java:12)
Exception in thread "main" java.lang.NullPointerException
        at org.drools.agent.RuleAgent.refreshRuleBase(RuleAgent.java:301)
        at org.drools.agent.RuleAgent.configure(RuleAgent.java:285)
        at org.drools.agent.RuleAgent.init(RuleAgent.java:209)
        at org.drools.agent.RuleAgent.newRuleAgent(RuleAgent.java:177)
        at org.drools.agent.RuleAgent.newRuleAgent(RuleAgent.java:149)
        at org.drools.agent.RuleAgent.newRuleAgent(RuleAgent.java:217)
        at kijanowski.eu.GuvnorTest.main(GuvnorTest.java:12)
</pre>
<p>If you navigate with your browser to this URL, you will be asked to accept the non-signed certificate. In case of our test application, we need to trust the server by importing its public key to our (temporary) local keystore:</p>
<pre>
$ mkdir /data/ssl

# export the public key
$ keytool -export -alias guvnor -keystore $JBOSS_SERVER/conf/guvnor.keystore -file /data/ssl/out.cert

# you don't have to provide a password
Enter keystore password:  

*****************  WARNING WARNING WARNING  *****************
* The integrity of the information stored in your keystore  *
* has NOT been verified!  In order to verify its integrity, *
* you must provide your keystore password.                  *
*****************  WARNING WARNING WARNING  *****************

Certificate stored in file </data/ssl/out.cert>

# import this key to a local truststore
$ keytool -import -alias guvnor -file /data/ssl/out.cert -keystore /data/ssl/myKS

Enter keystore password:  qwerty
Owner: CN=localhost, OU=My Department, O=My Company, L=My City, ST=My State, C=US
Issuer: CN=localhost, OU=My Department, O=My Company, L=My City, ST=My State, C=US
Serial number: 48862cb6
Valid from: Tue Jul 22 20:53:42 CEST 2008 until: Wed Jul 22 20:53:42 CEST 2009
Certificate fingerprints:
         MD5:  81:FD:97:97:12:E7:2B:94:DA:62:35:11:2C:2B:4E:2B
         SHA1: 7E:B1:36:F4:C9:F9:45:5A:98:F2:F1:46:F6:58:E6:0D:81:46:EC:B5
Trust this certificate? [no]:  yes
Certificate was added to keystore
</pre>
<p>Now we have a keystore with a server&#8217;s key that we trust. To use this keystore, just start the drools application with the following property:</p>
<pre>
-Djavax.net.ssl.trustStore=/data/ssl/myKS
</pre>
<p>In Eclipse, click on GuvnorTest.java. From the menu Run -&gt; Open Run Dialog and then add this property to the VM arguments:</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2745062728/" title="Fig 2.3  eclipse by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3075/2745062728_7bac4326aa.jpg" width="500" height="443" alt="Fig 2.3  eclipse" /></a></p>
<div class="caption">Fig 2.3 eclipse</div>
<p>Now your Drools application runs in a secure environment.</p>
<p><a name="summary"></a></p>
<h2>Summary</h2>
<p>This article has shown how you can upgrade your BRMS, which should only be deployed out-of-the-box for testing purposes. For a multiuser environment with mission-critical applications, Guvnor should be tuned. Have a look at this <a href="http://blogs.sun.com/andreas/entry/no_more_unable_to_find">blog post</a>, the <a href="http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html">JSSE Reference Guide</a> or the <a href="http://java.sun.com/javase/6/docs/technotes/tools/solaris/keytool.html">key tool docs page</a> for more details about the tools we used. For LDAP browsing I recommend this <a href="http://www-unix.mcs.anl.gov/~gawor/ldap/index.html">user-friendly and light-weight tool</a>.
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.redhatmagazine.com/2008/08/14/jboss-drools-how-to-tuning-guvnor-part-2/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Open source telephony: a Fedora-based VoIP server with Asterisk</title>
		<link>http://www.redhatmagazine.com/2008/07/24/open-source-telephony-a-fedora-based-voip-server-with-asterisk/</link>
		<comments>http://www.redhatmagazine.com/2008/07/24/open-source-telephony-a-fedora-based-voip-server-with-asterisk/#comments</comments>
		<pubDate>Thu, 24 Jul 2008 22:37:47 +0000</pubDate>
		<dc:creator>W. Michael Petullo</dc:creator>
		
		<category>Fedora</category>

		<category>technical</category>

		<guid isPermaLink="false">http://www.redhatmagazine.com/2008/07/24/open-source-telephony-a-fedora-based-voip-server-with-asterisk/</guid>
		<description><![CDATA[Voice over Internet Protocol (VoIP) has emerged as a popular technology for modern voice communications. Many organizations have replaced their analog or proprietary digital telephone systems with VoIP-based solutions. This allows the consolidation of telephone services into an existing IP infrastructure. In addition, using IP to host voice services lets the organization leverage existing expertise&#8211;while [...]]]></description>
			<content:encoded><![CDATA[<p>Voice over Internet Protocol (VoIP) has emerged as a popular technology for modern voice communications. Many organizations have replaced their analog or proprietary digital telephone systems with VoIP-based solutions. This allows the consolidation of telephone services into an existing IP infrastructure. In addition, using IP to host voice services lets the organization leverage existing expertise&#8211;while retaining all of the network&#8217;s management advantages. Though not without its disadvantages, VoIP provides a compelling option to those looking for a telephone solution.</p>
<p>This article will present a simple VoIP solution using Asterisk, an open source private branch exchange (PBX) product. It will show you how to install Asterisk, configure it using its LDAP backend, and connect to it using the Ekiga software VoIP client and a Cisco 7900 Series VoIP telephone to make calls.<a id="more-986"></a></p>
<h2>Prerequisites</h2>
<dl class="adnote">
<dt>Important note about Asterisk:</dt>
<dd>Asterisk 1.6 Beta 10 has not yet been released. This version will fix a bug in Asterisk&#8217;s realtime LDAP code that causes the server to crash. In the meantime, one may compile the upcoming realtime LDAP module and install it for use with Fedora&#8217;s packages. In order to do this, follow the following instructions after installing the Fedora Asterisk packages:</p>
<ol>
<li>Execute &#8220;yum install openldap-devel&#8221;</li>
<li>Download the Asterisk 1.6 Beta 9 source from http://www.asterisk.org/ and extract it.</li>
<li>Enter the resulting asterisk-1.6.0-beta9 directory.</li>
<li>Apply the patch available at http://www.flyn.org/patches/asterisk-1.6.0-beta9-crashfix/asterisk-1.6.0-beta9-crashfix.patch.gz by downloading it and executing &#8220;gunzip -c PATH-TO-PATCH.gz | patch -p1&#8243; from within the asterisk-1.6.0-beta9 directory.</li>
<li>Execute &#8220;./configure &#8211;prefix=/usr &#8211;sysconf=/etc&#8221;</li>
<li>Execute &#8220;make res&#8221;</li>
<li>Execute &#8220;cp res/res_config_ldap.so /usr/lib/asterisk/modules/&#8221;</li>
</ol>
</dd>
</dl>
<p>There are a few prerequisites to discuss before covering the configuration of Asterisk. First, my instructions assume that you have already installed Fedora 9. Second, you must have an existing LDAP server. The LDAP schema I use in this article is compatible with Fedora Directory Server. I recommend using the <a href="http://www.freeipa.org/">FreeIPA</a> system to manage FDS. Fedora 9 provides packages for both FDS (fedora-ds-base) and FreeIPA (ipa-server). Finally, the machine hosting Asterisk must allow incoming UDP connections to ports 5060 (Session Initiation Protocol) and 69 (TFTP) to pass through its firewall.</p>
<dl class="adnote">
<dt>Note:</dt>
<dd>In general, the LDAP database is optional. All configurations may be stored in flat files on the Asterisk server&#8217;s disk. However, I have chosen to store my Asterisk users and extensions in an LDAP database. This is beneficial for organizations that wish to consolidate user information in LDAP. In addition, Asterisk&#8217;s realtime LDAP backend is new to 1.6. I hope to help fill the gap in existing documentation with this article.</dd>
</dl>
<p>Install the Asterisk components by executing the command <code>yum install asterisk asterisk-ldap asterisk-alsa asterisk-voicemail asterisk-voicemail-plain</code>. In order to configure a Cisco 7900 Series VoIP telephone, you will also need to execute <code>yum install tftp-server</code>, have an existing DHCP server (the one on an inexpensive wireless router will work fine) and a copy of Cisco&#8217;s Session Initiation Protocol (SIP) software for the phone.</p>
<dl class="adnote">
<dt>Note:</dt>
<dd>Depending on how you buy a Cisco telephone, you may need to purchase the rights to Cisco&#8217;s SIP software. Cisco telephones are typically sold with support for the proprietary SCCP protocol. In order to use the telephone with SIP, one must load a SIP flash image onto the telephone. This image may not have been included in the original telephone purchase. One way to obtain a license for the software is to purchase a Cisco SmartNet support package. Support for a 7900 Series telephone costs less than $10 and should be available from the vendor that sold you the telephone. I will cover installing the SIP software later in the article.</dd>
</dl>
<p>The Fedora project does not yet package Asterisk&#8217;s audio files because their copyright holder has not yet assigned a license to them (see Red Hat <a href="https://bugzilla.redhat.com/show_bug.cgi?id=428832">Bugzilla bug #428832</a>). These audio files are required to implement features like an echo test and voicemail. Fortunately, it is quite simple to install them. Visit the <a href="http://www.asterisk.org/">Asterisk website</a> and download the latest Asterisk 1.6 series release. Inside the Asterisk archive is another, <code>sounds/asterisk-core-sounds-en-gsm-1.4.11.tar.gz</code>. Extract this archive file into <code>/usr/share/asterisk/sounds</code>.</p>
<h2>Configuring the Asterisk PBX</h2>
<p>The Asterisk PBX has many features and is very flexible. In this section, I will focus on configuring four things: the SIP module, the extensions module, the external configuration engine, and the realtime LDAP backend. Once Asterisk is configured, I will demonstrate how to add SIP users and extensions to your LDAP database.</p>
<p>Asterisk provides a module to support the SIP protocol. SIP is used to set up and tear down a VoIP phone call. Though individual SIP users will be stored in an LDAP database, some global parameters will exist in the following configuration file. Save this configuration as <code>/etc/asterisk/sip.conf</code>:</p>
<pre>
[general]
videosupport=yes
allow=all
bindaddr=0.0.0.0
realm=example.com
</pre>
<dl>
<dt>videosupport</dt>
<dd>Enable support for video streaming.</dd>
<dt>allow</dt>
<dd>List of CODEC&#8217;s to allow.</dd>
<dt>bindaddr</dt>
<dd>IP address of the device to bind to. 0.0.0.0 ensures that Asterisk will listen on all available network devices.</dd>
<dt>realm</dt>
<dd>The authentication realm.</dd>
</dl>
<p>We will now move on to define extensions. In many cases, an extension is simply a telephone number that will be associated with a SIP user. Individual extensions, like SIP users, will be stored in an<br />
LDAP database. However, as with the SIP configuration, global parameters will be configured in a file,<br />
<code>/etc/asterisk/extensions.conf</code>. The following example supports the LDAP entries I will use in this article:</p>
<pre>
[users]
switch =&gt; Realtime/@

[demo]
switch =&gt; Realtime/@

[default]
include =&gt; users
include =&gt; demo
</pre>
<p>This example defines three contexts: default, users, and demo. The line <code>switch =&gt; Realtime/@</code> states that the extensions in a context will be defined using one of Asterisk&#8217;s realtime back-ends (in our case, LDAP). Each individual extension will be assigned to one of these contexts. As a collection of available extensions, contexts determine what happens when a user dials a phone number.</p>
<p>Asterisk&#8217;s external configuration engines are activated in <code>/etc/asterisk/extconfig.conf</code>.  The following example configures Asterisk to pull SIP and extension information using its real-time LDAP back-end:</p>
<pre>
[settings]
sipusers =&gt; ldap,"dc=example,dc=com",sip
sippeers =&gt; ldap,"dc=example,dc=com",sip
extensions =&gt; ldap,"dc=example,dc=com",extensions
 </pre>
<p>This example causes Asterisk to activate its real-time LDAP back-end look for SIP users, SIP peers, and extensions in the LDAP database. In addition, the base DN used for queries is specified.</p>
<dl class="adnote">
<dt>Note:</dt>
<dd>All of the examples in this article will use <em>dc=example,dc=com</em> as the LDAP database&#8217;s base DN. This should be replaced with your organization&#8217;s base DN.</dd>
</dl>
<p>The final configuration file we will write configures Asterisk&#8217;s real-time LDAP back-end and should be saved as <code>/etc/asterisk/res_ldap.conf</code>.</p>
<pre>
[_general]
url=ldaps://ldap.example.com:636
protocol=3
basedn="dc=example,dc=com"
user=cn=Directory Manager
pass=LDAP-PASSWORD

[config]
additionalFilter=(objectClass=AstConfig)
filename = AstConfigFilename
category = AstConfigCategory
variable_name = AstConfigVariableName
variable_value = AstConfigVariableValue
cat_metric = AstConfigCategoryMetric
commented = AstConfigCommented

[extensions]
context  =  AstContext
exten  =  AstExtension
priority = AstPriority
app = AstApplication
appdata = AstApplicationData
additionalFilter=(objectClass=AsteriskExtension)

[sip]
name = cn
amaflags = AstAccountAMAFlags
callgroup = AstAccountCallGroup
callerid = AstAccountCallerID
canreinvite = AstAccountCanReinvite
context = AstAccountContext
dtmfmode = AstAccountDTMFMode
fromuser = AstAccountFromUser
fromdomain = AstAccountFromDomain
fullcontact = AstAccountFullContact
host = AstAccountHost
ipaddr = AstAccountIPAddress
insecure = AstAccountInsecure
mailbox = AstAccountMailbox
md5secret = AstAccountRealmedPassword
nat = AstAccountNAT
deny = AstAccountDeny
permit = AstAccountPermit
pickupgroup = AstAccountPickupGroup
port = AstAccountPort
qualify = AstAccountQualify
restrictcid = AstAccountRestrictCID
rtptimeout = AstAccountRTPTimeout
rtpholdtimeout = AstAccountRTPHoldTimeout
type = AstAccountType
disallow = AstAccountDisallowedCodec
allow = AstAccountAllowedCodec
MusicOnHold = AstAccountMusicOnHold
regseconds = AstAccountExpirationTimestamp
regcontext = AstAccountRegistrationContext
regexten = AstAccountRegistrationExten
CanCallForward = AstAccountCanCallForward
defaultuser = AstAccountDefaultUser
regserver = AstAccountRegistrationServer
additionalFilter = (objectClass=AsteriskSIPUser)
</pre>
<p>The <em>config</em>, <em>extensions</em>, and <em>sip</em> sections of this file map Asterisk configuration options to LDAP attributes and do not need to be customized. The <em>_general</em> section contains the following options:</p>
<dl>
<dt>url</dt>
<dd>The URL pointing to the organization&#8217;s LDAP server. The example above uses LDAP over SSL on port 636.</dd>
<dt>protocol</dt>
<dd>The LDAP version to use.</dd>
<dt>basedn</dt>
<dd>The base DN used for queries.</dd>
<dt>user</dt>
<dd>The name of the LDAP administrator. <em>Directory Manager</em> is the name used by the Fedora Directory Server.</dd>
<dt>pass</dt>
<dd>The LDAP directory manager&#8217;s password.</dd>
</dl>
<h2>Configuring LDAP</h2>
<p>With the exception of actual users and extensions, our simple Asterisk installation is fully configured. We will now finish our installation by adding users and extensions to our LDAP database. First, we need to install an Asterisk schema for LDAP at <code>/etc/dirsrv/slapd-DOMAIN/schema/99asterisk.ldif</code>. After the schema has been installed, restart FDS using <code>service dirsrv restart</code>.</p>
<pre>
dn: cn=schema
#
attributeTypes: (
  NAME 'AstContext'
  DESC 'Asterisk Context'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstExtension'
  DESC 'Asterisk Extension'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstPriority'
  DESC 'Asterisk Priority'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstApplication'
  DESC 'Asterisk Application'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstApplicationData'
  DESC 'Asterisk Application Data'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountAMAFlags'
  DESC 'Asterisk Account AMA Flags'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountCallerID'
  DESC 'Asterisk Account CallerID'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountContext'
  DESC 'Asterisk Account Context'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountMailbox'
  DESC 'Asterisk Account Mailbox'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstMD5secret'
  DESC 'Asterisk Account MD5 Secret'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountDeny'
  DESC 'Asterisk Account Deny'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountPermit'
  DESC 'Asterisk Account Permit'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountQualify'
  DESC 'Asterisk Account Qualify'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountType'
  DESC 'Asterisk Account Type'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountDisallowedCodec'
  DESC 'Asterisk Account Disallowed Codec'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountExpirationTimestamp'
  DESC 'Asterisk Account Allowed Codec'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountRegistrationContext'
  DESC 'Asterisk Account AMA Flags'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountRegistrationExten'
  DESC 'Asterisk Account AMA Flags'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountNoTransfer'
  DESC 'Asterisk Account AMA Flags'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountCallGroup'
  DESC 'Asterisk Account Call Group'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountCanReinvite'
  DESC 'Asterisk Account Can Reinvite'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountDTMFMode'
  DESC 'Asterisk Account DTMF Flags'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountFromUser'
  DESC 'Asterisk Account From User'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountFromDomain'
  DESC 'Asterisk Account From Domain'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountFullContact'
  DESC 'Asterisk Account Full Contact'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountHost'
  DESC 'Asterisk Account Host'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountInsecure'
  DESC 'Asterisk Account Insecure'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountNAT'
  DESC 'Asterisk Account NAT'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountPickupGroup'
  DESC 'Asterisk Account PickupGroup'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountPort'
  DESC 'Asterisk Account Port'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountRestrictCID'
  DESC 'Asterisk Restrict CallerID'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountRTPTimeout'
  DESC 'Asterisk RTP Timeout'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountRTPHoldTimeout'
  DESC 'Asterisk RTP Hold Timeout'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountRealmedPassword'
  DESC 'Asterisk RTP Hold Timeout'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountAllowedCodec'
  DESC 'Asterisk Account Allowed Codec'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountMusicOnHold'
  DESC 'Asterisk Account Allowed Codec'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountCanCallForward'
  DESC 'Asterisk Can CAll Forward'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountSecret'
  DESC 'Asterisk Can CAll Forward'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountName'
  DESC 'Asterisk Account Username'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstConfigFilename'
  DESC 'Asterisk LDAP Configuration Filename'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstConfigCategory'
  DESC 'Asterisk LDAP Configuration Category'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstConfigCategoryMetric'
  DESC 'Asterisk LDAP Configuration Category Metric'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstConfigVariableName'
  DESC 'Asterisk LDAP Configuration Variable Name'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstConfigVariableValue'
  DESC 'Asterisk LDAP Configuration Variable Value'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstConfigCommented'
  DESC 'Asterisk LDAP Configuration Commented'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountIPAddress'
  DESC 'Asterisk Account IP Address'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountDefaultUser'
  DESC 'Asterisk Account Default User'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstAccountRegistrationServer'
  DESC 'Asterisk Account Registration Server'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
objectClasses: (
  NAME 'AsteriskExtension'
  DESC 'PBX Extension Information for Asterisk'
  SUP top
  AUXILIARY
  MUST cn
  MAY ( AstContext $ AstExtension $ AstPriority $ AstApplication
      $ AstApplicationData )
  )
#
objectClasses: (
  NAME 'AsteriskIAXUser'
  DESC 'IAX2 User information for Asterisk'
  SUP AsteriskExtension
  AUXILIARY
  MUST cn
  MAY ( AstAccountAMAFlags $ AstAccountCallerID $ AstAccountContext
      $ AstAccountFullContact $ AstAccountHost $ AstAccountMailbox $ AstMD5secret
      $ AstAccountDeny $ AstAccountPermit $ AstAccountPort $ AstAccountQualify
      $ AstAccountType $ AstAccountDisallowedCodec $ AstAccountExpirationTimestamp
      $ AstAccountRegistrationContext$ AstAccountRegistrationExten
      $ AstAccountNoTransfer $ AstAccountName )
  )
#
objectClasses: (
  NAME 'AsteriskSIPUser'
  DESC 'SIP User information for Asterisk'
  SUP AsteriskExtension
  AUXILIARY
  MUST cn
  MAY ( AstAccountAMAFlags $ AstAccountCallGroup $ AstAccountCallerID
      $ AstAccountCanReinvite $ AstAccountContext $ AstAccountDefaultUser
      $ AstAccountDTMFMode $ AstAccountFromUser $ AstAccountFromDomain
      $ AstAccountFullContact $ AstAccountHost $ AstAccountInsecure
      $ AstAccountIPAddress $ AstAccountMailbox $ AstAccountRealmedPassword
      $ AstAccountNAT $ AstAccountDeny $ AstAccountPermit $ AstAccountPickupGroup
      $ AstAccountPort $ AstAccountQualify $ AstAccountRestrictCID
      $ AstAccountRTPTimeout $ AstAccountRTPHoldTimeout $ AstAccountType
      $ AstAccountDisallowedCodec $ AstAccountAllowedCodec $ AstAccountMusicOnHold
      $ AstAccountExpirationTimestamp $ AstAccountRegistrationContext
      $ AstAccountRegistrationExten $ AstAccountRegistrationServer
      $ AstAccountCanCallForward $ AstAccountSecret $ AstAccountName )
  )
#
objectClasses: (
  NAME 'AsteriskConfig'
  DESC 'Asterisk configuration Information'
  SUP top
  AUXILIARY
  MUST cn
  MAY ( AstConfigFilename $ AstConfigCategory $ AstConfigCategoryMetric
      $ AstConfigVariableName $ AstConfigVariableValue $ AstConfigCommented )
  )
</pre>
<p>We may now create two SIP users, one for our Ekiga installation and the other for our Cisco telephone. If you write the following LDIF into a file, you may load it into the LDAP database using <code>ldapadd -x -D "cn=Directory Manager" -W -f PATH-TO-LDIF</code>.</p>
<pre>
dn: ou=sippeers,dc=example,dc=com
ou: sippeers
objectClass: top
objectClass: organizationalUnit

dn: cn=user1,ou=sippeers,dc=example,dc=com
objectClass: top
objectClass: AsteriskSIPUser
cn: user1
AstAccountCallerID: 2001
AstAccountHost: dynamic
AstAccountRealmedPassword: {MD5}a94775781e5bb7d3e4ec047c56f0acc5
AstAccountContext: default
AstAccountType: friend

dn: cn=user2,ou=sippeers,dc=example,dc=com
objectClass: top
objectClass: AsteriskSIPUser
cn: user2
AstAccountCallerID: 2002
AstAccountHost: dynamic
AstAccountRealmedPassword: {MD5}3c7806fa6e6c3416d57f2de223cdea5d
AstAccountContext: default
AstAccountType: friend
</pre>
<p>The example above defines a minimal SIP user. The attributes used are:</p>
<dl>
<dt>cn</dt>
<dd>The SIP user&#8217;s account name.</dd>
<dt>AstAccountCallerID</dt>
<dd>The user&#8217;s caller ID information.</para></dd>
<dt>AstAccountHost</dt>
<dd>The user&#8217;s host. The <em>dynamic</em> keyword allows a user&#8217;s telephone to dynamically register its IP address by logging into Asterisk.</dd>
<dt>AstAccountRealmedPassword</dt>
<dd>The account&#8217;s password. The hash of a user&#8217;s password may be generated by executing <code>echo -n "SIPUSER:example.com:PASSWORD" | md5sum</code></dd>
<dt>AstAccountContext</dt>
<dd>The context this user will exist in. This determines which extension context applies to this user. Extension contexts were defined in <code>extensions.conf</code>.</dd>
<dt>AstAccountType</dt>
<dd>Defines the type of client. Asterisk sends calls to peers (i.e., a SIP provider). The user type may place calls only. Friends are both peers and users.</dd>
</dl>
<p>Once a user is defined, one may test Asterisk. Start the Asterisk service by executing <code>service asterisk start</code> as root. Execute an Asterisk shell using the command <code>asterisk -rv</code>. At Asterisk&#8217;s prompt, execute <code>sip show peer user1 load</code>. Asterisk should print all of the information for user1.</p>
<p>Once you have defined a user, you may start writing your dial plan. First, we will associate a phone number with each user. Use <code>ldapadd</code> to add the following to your LDAP database:</p>
<pre>
dn: ou=extensions,dc=example,dc=com
ou: extensions
objectClass: top
objectClass: organizationalUnit

dn: cn=2001-1,ou=extensions,dc=example,dc=com
cn: 2001-1
objectClass: top
objectClass: AsteriskExtension
AstContext: users
AstExtension: 2001
AstPriority: 1
AstApplication: Dial
AstApplicationData: SIP/user1,20

dn: cn=2002-1,ou=extensions,dc=example,dc=com
cn: 2002-1
objectClass: top
objectClass: AsteriskExtension
AstContext: users
AstExtension: 2002
AstPriority: 1
AstApplication: Dial
AstApplicationData: SIP/user2,20
</pre>
<dl>
<dt>AstContext</dt>
<dd>The context in which this extension exists.</dd>
<dt>AstExtension</dt>
<dd>The extension ID (e.g., what you dial to reach this extension).</dd>
<dt>AstPriority</dt>
<dd>Each extension can execute several commands when it receives a call. The priority determines the order in which commands are executed.</dd>
<dt>AstApplication</dt>
<dd>The command to execute when the extension receives a call. The command <em>Dial</em> connects to another Asterisk user.</dd>
<dt>AstApplicationData</dt>
<dd>Arguments, separated with commas (without spaces), and passed to a command. The <code>Dial</code> has two arguments. The first is the user that the call will be connected to. The second is the number of seconds to wait before quitting.</dd>
</dl>
<p>In order to facilitate testing our installation, we will define the following additional extension. This extension executes three commands to implement an echo test that may be reached by dialing &#8216;600.&#8217;</p>
<pre>
dn: cn=600-1,ou=extensions,dc=example,dc=com
cn: 600-1
objectClass: top
objectClass: AsteriskExtension
AstContext: demo
AstExtension: 600
AstPriority: 1
AstApplication: Playback
AstApplicationData: demo-echotest

dn: cn=600-2,ou=extensions,dc=example,dc=com
cn: 600-2
objectClass: top
objectClass: AsteriskExtension
AstContext: demo
AstExtension: 600
AstPriority: 2
AstApplication: Echo

dn: cn=600-3,ou=extensions,dc=example,dc=com
cn: 600-3
objectClass: top
objectClass: AsteriskExtension
AstContext: demo
AstExtension: 600
AstPriority: 3
AstApplication: Playback
AstApplicationData: demo-echodone
</pre>
<p> After schema edits, FDS must be restarted using <code>service dirsrv restart</code>.</p>
<p>The basic Asterisk configuration is now complete. Restart the Asterisk service using <code>service asterisk restart</code>.</p>
<h2>Configuring the Ekiga VoIP client</h2>
<p>Ekiga is a software VoIP application that supports SIP. Its configuration is straightforward. The first time Ekiga is executed by a user, it provides a wizard that allows one to configure the user&#8217;s full name,<br />
ekiga.net account (this is not required for the purpose of this article), connection type, audio devices, and video devices. Once the software is configured, setup a SIP account by clicking on <strong>Edit&rarr;Accounts</strong> and selecting add. Figure 1 displays an account configured for our SIP user identified as <em>user1</em>.</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2699545118/" title="asterisk-ldap-figure1-en by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3028/2699545118_2022a665d5_o.png" width="352" height="268" alt="asterisk-ldap-figure1-en" /></a></p>
<div class=”caption”>Figure 1. Ekiga Account Configuration</div>
<p>After an account is set up, it must be activated. Figure 2 shows that the account I just configured is displayed in Ekiga&#8217;s list. Click the checkbox to activate the account.</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2699545130/" title="asterisk-ldap-figure2-en by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3259/2699545130_c9482ab961.jpg" width="500" height="203" alt="asterisk-ldap-figure2-en" /></a></p>
<div class=”caption”>Figure 2. Ekiga Account Activation</div>
<p>Figure 3 shows Ekiga&#8217;s main interface window. Here, you can see how many accounts have been registered. In order to test your configuration, make a SIP connection to extension 600 and perform an echo test.</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2699545132/" title="asterisk-ldap-figure3-en by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3032/2699545132_4efc8ce9ff_o.png" width="300" height="421" alt="asterisk-ldap-figure3-en" /></a></p>
<div class=”caption”>Figure 3. Ekiga Primary Interface</div>
<h2>Configuring a Cisco VoIP telephone</h2>
<p>As mentioned before, using a Cisco phone to connect to Asterisk using SIP requires Cisco&#8217;s SIP software. When a Cisco 7900 Series telephone boots, it will try to configure itself using TFTP. We will place some firmware and configuration files on our server so that the phone will boot properly for our installation.</p>
<p>In order to enable TFTP on the Asterisk server, edit <code>/etc/xinetd.d/tftp</code> and set <em>disable</em> to <em>no</em>. The TFTP daemon is configured on Fedora to serve files out of <code>/var/lib/tftpboot</code>. After you have modified its configuration, restart xinetd with the command <code>service xinetd restart</code>. Cisco&#8217;s SIP software package provides the files P0S3-08-2-00.sb2, P0S3-08-2-00.sbn and P0S3-08-2-00.loads. Copy these files to <code>/var/lib/tftpboot</code>.</p>
<dl class="adnote">
<dt>Note:</dt>
<dd>The filenames used by your Cisco firmware may be slightly different. Cisco&#8217;s naming convention is P0S3-xxx-y-zz, where x is the major version, y is the minor version, and z is the subversion. P0S3 indicates SIP firmware, P003 indicates SCCP firmware, and P0M3 indicates MGCP firmware.</dd>
</dl>
<p>When booting SIP firmware, the telephone will look for three configuration files: OS79XX.TXT, SIPDefault.cnf, and SIP<em>MAC-ADDRESS</em>.cnf. These files should also<br />
be placed in <code>/var/lib/tftpboot</code>.</p>
<p>OS79XX.TXT contains one line: the firmware version to load. Mine contains <em>P0S3-08-2-00</em>.</p>
<p>SIPDefault.cnf contains configurations common to all SIP telephones. The following example lists the image version and the address of our Asterisk server, and instructs the telephones to register with Asterisk:</p>
<pre>
image_version: P0S3-08-2-00 ;
proxy1_address: 192.168.0.10 ;
proxy_register: "1" ;
</pre>
<p>Per-telephone configurations are stored in a file named after the telephone&#8217;s MAC address. The telephone&#8217;s MAC address may be found in its configuration menu. This configuration will correspond with the SIP information found in LDAP and should be saved as SIP<em>MAC-ADDRESS</em>.cnf:</p>
<pre>
line1_name : user2
line1_authname : user2
line1_password : PLAINTEXT-PASSWORD
</pre>
<p>The first step in preparing the telephone is to clear any existing configuration. This may be done by pressing <code>**# settings 3</code> to access an unlocked <em>Network<br />
Configuration</em> menu. Here, find the <em>Erase Configuration</em> menu item and select it. Restart the phone and return to the <em>Network Configuration</em> menu. Ensure <em>DHCP Enabled</em> is set. Reboot the phone.</p>
<p>The phone will pull its network configuration from DHCP and should now respond to pings. Return to the <em>Network Configuration</em> menu and deactivate DHCP. Select the <em>TFTP Server 1</em> option and set it to your Asterisk / TFTP server. Do not disable DHCP or manually set a TFTP server if your DHCP server provides your TFTP server&#8217;s IP address. Reboot the telephone and it should load the SIP software and configuration. As the phone boots, you may see it request files using TFTP by watching the Asterisk server&#8217;s <code>/var/log/messages</code> log.</p>
<p>Once the telephone boots, test its configuration by dialing <code>600</code>. This will execute an echo test. After the echo test, you may make a Cisco telephone to Ekiga call by dialing 2001.</p>
<h2>Configuring voicemail</h2>
<p>In this section, we will cover configuring Asterisk to provide a voicemail system. The configuration process is similar to that of SIP users and extensions, so this should also serve to reinforce the techniques covered in those sections. We will configure the voicemail module, the external<br />
configuration engine, and the real-time LDAP back-end. After the configuration files are setup, we will add information to the LDAP database.</p>
<p><code>/etc/asterisk/voicemail.conf</code> is similar to <code>sip.conf</code> and<br />
<code>extensions.conf</code>. This file configures the voicemail module. As with SIP users and extensions, we will configure global parameters in a file, leaving extension-specific parameters to be configured in LDAP:</p>
<pre>
[general]
searchcontexts=yes
</pre>
<p>Add the following line to <code>/etc/asterisk/extconfig.conf</code> in order to activate the real-time LDAP back-end for voicemail:</p>
<pre>
voicemail =&gt; ldap,"dc=example,dc=com",voicemail
 </pre>
<p>Map Asterisk voicemail configuration options to LDAP attributes by adding the following to <code>/etc/asterisk/res_ldap.conf</code>:</p>
<pre>
[voicemail]
context = AstVMContext
mailbox = AstVMMailbox
password = AstVMPassword
fullname = AstVMFullname
email = AstVMEmail
pager = AstVMPager
tz = AstVMTz
attach = AstVMAttach
saycid = AstVMSaycid
dialout = AstVMDialout
callback = AstVMCallback
review = AstVMReview
operator = AstVMOperator
envelope = AstVMEnvelope
sayduration = AstVMSayduration
saydurationm = AstVMSaydurationm
sendvoicemail = AstVMSendvoicemail
delete = AstVMDelete
nextaftercmd = AstVMNextastercmd
forcename = AstVMForcename
forcegreetings = AstVMForcegreetings
hidefromdir = AstVMHidefromdir
stamp = AstVMStamp
additionalFilter = (objectClass=AsteriskVoicemail)
</pre>
<p>Now we will move to updating our LDAP database. First, we must add to the LDAP schema that we installed earlier. The following will add support for the attributes required to provide basic voicemail:</p>
<pre>
attributeTypes: (
  NAME 'AstVMMailbox'
  DESC 'Asterisk Voicemail Mailbox'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstVMPassword'
  DESC 'Asterisk Voicemail Password'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstVMFullname'
  DESC 'Asterisk Voicemail Fullname'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
attributeTypes: (
  NAME 'AstVMEmail'
  DESC 'Asterisk Voicemail Email'
  EQUALITY caseIgnoreMatch
  SUBSTR caseIgnoreSubstringsMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  )
#
objectClasses: (
  NAME 'AsteriskVoicemail'
  DESC 'Voicemail information for Asterisk'
  SUP top
  MUST cn
  MAY ( AstContext $ AstVMMailbox $ AstVMPassword $ AstVMFullname
      $ AstVMEmail )
  )
</pre>
<p>Add the following LDIF to the LDAP database using <code>ldapadd</code> in order to add voicemail support for the extensions 2001 and 2002:</p>
<pre>
dn: ou=voicemail,dc=example,dc=com
ou: voicemail
objectClass: top
objectClass: organizationalUnit

dn: cn=2001,ou=voicemail,dc=example,dc=com
cn: 2001
objectClass: top
objectClass: AsteriskVoicemail
AstContext: users
AstVMMailbox: 2001
AstVMPassword: 1234
AstVMFullname: User 1
AstVMEmail: user1@example.org

dn: cn=2002,ou=voicemail,dc=example,dc=com
cn: 2002
objectClass: top
objectClass: AsteriskVoicemail
AstContext: users
AstVMMailbox: 2002
AstVMPassword: 1234
AstVMFullname: User 2
AstVMEmail: user2@example.org

dn: cn=2000-1,ou=extensions,dc=example,dc=com
cn: 2000-1
objectClass: top
objectClass: AsteriskExtension
AstContext: users
AstExtension: 2000
AstPriority: 1
AstApplication: Voicemailmain

dn: cn=2001-2,ou=extensions,dc=example,dc=com
cn: 2001-2
objectClass: top
objectClass: AsteriskExtension
AstContext: users
AstExtension: 2001
AstPriority: 2
AstApplication: Voicemail
AstApplicationData: 2001,u

dn: cn=2002-2,ou=extensions,dc=example,dc=com
cn: 2002-2
objectClass: top
objectClass: AsteriskExtension
AstContext: users
AstExtension: 2002
AstPriority: 2
AstApplication: Voicemail
AstApplicationData: 2002,u
</pre>
<p>The second entry defines a mailbox for extension 2001. It uses the following options:</p>
<dl>
<dt>AstContext</dt>
<dd>The context in which this mailbox exists.</dd>
<dt>AstVMMailbox</dt>
<dd>The extension that this mailbox corresponds to.</dd>
<dt>AstVMPassword</dt>
<dd>The password used to access the mailbox.</dd>
<dt>AstVMFullname</dt>
<dd>The full name of the mailbox owner.</dd>
<dt>AstVMEmail</dt>
<dd>The mailbox owner&#8217;s email address.</dd>
</dl>
<p>Entity four defines an extension, 2000, that will provide access to the voicemail system when dialed.</p>
<p>Entity five adds a command to extension 2001 that executes the <code>Voicemail</code> and provides access to the voicemail system. This command will execute if the <code>Dial</code> command defined earlier is not answered within 20 seconds.</p>
<p>To test the voicemail system, use Ekiga to dial <code>2002</code>. If you don&#8217;t answer the call on the other end, then you should be directed to Asterisk&#8217;s voicemail system. To listen to new voicemail, dial <code>2000</code>.</p>
<h2>Conclusion</h2>
<p>This article demonstrated how to install Asterisk on a Fedora system and configure SIP users, extensions, and voicemail. We installed all user data into an LDAP database in order to keep it consolidated. </p>
<p>Asterisk has far too many features to cover in one article. The VoIP Wiki has a <a href="http://www.voip-info.org/wiki/view/Asterisk">section dedicated to Asterisk</a> and is a great place to learn more. The VoIP Wiki covers topics such as hooking Asterisk up to the standard public-switched telephone network using direct inward dialing and automatically emailing voicemail messages to users. You can even find information on how to route calls to a user based on his Jabber presence. Clearly, Asterisk is capable of a wide range of applications. And, perhaps best of all, it&#8217;s entirely open source!
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.redhatmagazine.com/2008/07/24/open-source-telephony-a-fedora-based-voip-server-with-asterisk/feed/</wfw:commentRss>
		</item>
		<item>
		<title>NetworkManager: Secret weapon for the Linux road warrior</title>
		<link>http://www.redhatmagazine.com/2008/07/22/networkmanager-secret-weapon-for-the-linux-road-warrior/</link>
		<comments>http://www.redhatmagazine.com/2008/07/22/networkmanager-secret-weapon-for-the-linux-road-warrior/#comments</comments>
		<pubDate>Tue, 22 Jul 2008 22:21:19 +0000</pubDate>
		<dc:creator>Kyle Gonzales</dc:creator>
		
		<category>Fedora</category>

		<category>Red Hat Enterprise Linux</category>

		<category>technical</category>

		<guid isPermaLink="false">http://www.redhatmagazine.com/2008/07/22/networkmanager-secret-weapon-for-the-linux-road-warrior/</guid>
		<description><![CDATA[For years I have envied how easy my Windows- and Mac-based peers had it when traveling with their laptops.  They connect to hotspots with ease, get online while I was still logging into root and running some tools.  It just wasn&#8217;t fair.  I wanted an integrated easy-to-use tool that did not require [...]]]></description>
			<content:encoded><![CDATA[<p>For years I have envied how easy my Windows- and Mac-based peers had it when traveling with their laptops.  They connect to hotspots with ease, get online while I was still logging into root and running some tools.  It just wasn&#8217;t fair.  I wanted an integrated easy-to-use tool that did not require bringing up a shell or logging into root.  </p>
<p>I now have that tool in NetworkManager.  In this article I will explain what NetworkManager is, what capabilities exist in the tool (in both Fedora and Red Hat Enterprise Linux), and what you can do to extend it to give you more control over your system than before.</p>
<h2>What is NetworkManager?</h2>
<p>NetworkManager is a software utility that allows a desktop user to manage wired, wireless, modem, WWAN/3G, and VPN network connectivity from a single source. It does not require root access or manual editing of configuration files.  </p>
<p>NetworkManager started as a Gnome project and initially appeared in Fedora. It is now supported on multiple desktop environments (Gnome, KDE, Xfce, etc.) and in multiple distributions (Fedora, SuSE, Ubuntu, Gentoo, Debian, etc.).  NetworkManager uses dbus and hal to provide network status updates to other desktop applications, allowing them to alter their operation based on this information.  For instance, if NetworkManager shows the network is offline, then apps like Evolution and Pidgin will put themselves into offline mode andwait for the network to come online.<a id="more-981"></a></p>
<h2>How is the NetworkManager software deployed on the system?</h2>
<p>NetworkManager is deployed in two parts.  The first part is the NetworkManager daemon, which is found in the package NetworkManager.  This daemon should be set to start while the system is booting.  This can be accomplished by entering the following command as root:</p>
<pre>
    # /sbin/chkconfig NetworkManager on
</pre>
<p>You can also start NetworkManager manually by entering the following command as root:</p>
<pre>
    # /sbin/service NetworkManager start
</pre>
<p>The second part is the user client, which normally takes the form of an applet.  This applet (nm-applet) can be found in the NetworkManager-gnome package, and should be part of the basic Gnome desktop installation.  You will not need to add this applet to your desktop.  Gnome will add the nm-applet control to the Notification Area applet when the NetworkManager daemon is active.</p>
<h2>How does NetworkManager work?</h2>
<p>For the user, most everything will be done via the NetworkManager applet.  Exactly what needs to be done depends on the type of networking the user needs to activate.</p>
<h3>Wired network</h3>
<p>If the system the user is logged into is on a wired network (Ethernet), the user does not need to do anything.  NetworkManager will look for the link on the network port.  When the link is active, it will bring up the interface and then ask for network information via DHCP.</p>
<h3>Wireless Network</h3>
<p>If the user is trying to connect via wireless, NetworkManager is especially helpful.  As long as the wireless device is active, NetworkManager will scan for available networks and will attempt to connect to the last network you connected to that it can see.  If the network it is trying to connect to is a secure network (using WEP, WPA, WPA2, or LEAP) it will request the appropriate security information.  Once the information is entered, NetworkManager will try to store this information into the GNOME keyring manager.</p>
<p>To connect to a different network than the one that NetworkManager chooses, simply click on the applet and choose a different wireless network.</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2693123353/" title="NM-Wireless by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3272/2693123353_311e143803_o.png" width="398" height="430" alt="NM-Wireless" /></a></p>
<h3>WWAN network (3G/EVDO/HSDPA/RTTx1/EDGE)</h3>
<p>With the release of NetworkManager 0.70, users can now choose WWAN networking.  Most of these cards require activation in Windows, but NetworkManager can handle the auto-configuration some cards need for use under Linux.  Other cards may still require some minimal account information to activate and use.  </p>
<p>If the card is plugged in when NetworkManager starts, it will be autodetected and an attempt to auto-configure the card will be made when you request a connection to the network.  If auto-configuration is successful, the user can then just select the card in the applet menu and connect.</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2693937432/" title="NM-CDMA by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3151/2693937432_99df6a5ed0_o.png" width="398" height="301" alt="NM-CDMA" /></a></p>
<h3>VPN connectivity</h3>
<p>Once a successful network connection has been made, the user can also use NetworkManager to activate a VPN connection.  Currently, there are modules providing support for OpenVPN and Cisco (via vpnc) VPN connectivity.  </p>
<p>The VPN connection will be configured, activated, and deactivated via the applet.  Username, password, group passwords, and other information can be stored in the GNOME keyring manager, or the user can choose to be prompted to enter some—or all—of the information at each login.</p>
<h2>What else can NetworkManager do?</h2>
<p>Beside managing your network connectivity, NetworkManager has another key feature. NetworkManager can run scripts when there is a network state change on any interface, using the network interface and the up/down state as variables. In prior releases, this functionality was provided by a separate daemon called NetworkManagerDispatcher. As of NetworkManager 0.70 in Fedora 9, this functionality is now integrated into NetworkManager itself.</p>
<p>In Bash scripts written for NetworkManager, the variable <code>$1</code> equals the interface whose state has changed and triggered the script. Variable <code>$2</code> equals the state of the interface (up or down). No other variables are needed.</p>
<p>Let&#8217;s take a look at one of the scripts that is included with Fedora 9:</p>
<pre>
# cat /etc/NetworkManager/dispatcher.d/05-netfs

#!/bin/sh

export LC_ALL=C

if [ "$2" = "down" ]; then
        /sbin/ip route ls | grep -q ^default || {
                [ -f /var/lock/subsys/netfs ] &#038;&#038; /etc/rc.d/init.d/netfs stop
        }
fi

if [ "$2" = "up" ]; then
        /sbin/ip -o route show dev "$1" | grep -q '^default' &#038;&#038; {
                /sbin/chkconfig netfs &#038;&#038; /etc/rc.d/init.d/netfs start
        }
fi
</pre>
<p>When an interface comes up and adds itself as the default route, the script starts the netfs service. This script also stops the netfs service when an interface goes down and no default route remains.  Effectively, this will mount your NFS and CIFS shares when you have access to the network, and will unmount those same shares when the network goes down.  Using this script as an example, you can easily write your own scripts to run various commands as the network state changes.</p>
<h2>How can I best use NetworkManager in the field?</h2>
<p>Now that you have a good idea of how NetworkManager works and what it can do, let&#8217;s talk about how to best use NetworkManager in the field. Now that you have NetworkManager managing your network connectivity, make sure your network interfaces are not trying to start on boot. Nothing is more annoying than having your laptop tell you that your wired network is not available when you are sitting on a plane.  If you are using NetworkManager 0.70 (currently in Fedora 9), you should also disable the network service itself, as it may conflict with NetworkManager.</p>
<p>You can go further, writing NetworkManager scripts to activate various services only when they are needed. Many of the init scripts in Linux make the assumption that your system is a server or a workstation with continuous access to the network. Things like ntp, cups, sshd, even rhnsd do not need to be running while you have no network connectivity. These services can be disabled, set to run only when NetworkManager starts them via a custom script on a network state change. </p>
<p>Using the previously posted script as a guide, a script to manage sshd might look like this:</p>
<pre>
# cat /etc/NetworkManager/dispatcher.d/10-sshd

#!/bin/sh
#
# Start and stop sshd based on network availability using NetworkManager
#

export LC_ALL=C

if [ "$2" = "down" ]; then
        /sbin/ip route ls | grep -q ^default || {
                [ -f /var/lock/subsys/sshd ] &#038;&#038; /etc/rc.d/init.d/sshd stop
        }
fi

if [ "$2" = "up" ]; then
        /sbin/ip -o route show dev "$1" | grep -q '^default' &#038;&#038; {
                /sbin/chkconfig sshd &#038;&#038; /etc/rc.d/init.d/sshd start
        }
fi
</pre>
<p>You could substitute &#8220;rhnsd&#8221; or &#8220;cups&#8221; for &#8220;sshd&#8221;, and the script should work equally well for those tasks..</p>
<p>If you are a administrator tasked with managing Red Hat or Fedora systems of remote employees, the scripting functionality can be even more handy. You can write a script that looks for the activation of the VPN interface then sends an email letting you know the system is online. You could have the system check in with a Satellite server located within your firewall, installing updates you previously scheduled for it. The possible uses here are many.</p>
<h2>The student is now the master</h2>
<p>No longer do I envy my Windows-based peers and their easy  mobile connectivity. NetworkManager is constantly impressing me, adding functionality and allowing me to be more efficient on the road. This Swiss Army knife of Linux networking gives me the control I need over my connectivity whether at home, coffee house, or airport. Now that you know what NetworkManager is, how it works, and how best to use it, try it out of your own system. I trust you will find NetworkManager works as well for you as it did for me.</p>
<h2>More information</h2>
<ol>
<li><a href="http://www.gnome.org/projects/NetworkManager">NetworkManager main project page</a></li>
<li><a href="http://fedoraproject.org/wiki/Tools/NetworkManager">NetworkManager in Fedora</a></li>
<li><a href="http://www.freedesktop.org/wiki/Software/dbus">dbus</a> and <a href="http://www.freedesktop.org/wiki/Software/hal<br />
">hal</a></li>
<li><a href="http://en.opensuse.org/Projects/KNetworkManager">KNetworkManager</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.redhatmagazine.com/2008/07/22/networkmanager-secret-weapon-for-the-linux-road-warrior/feed/</wfw:commentRss>
		</item>
		<item>
		<title>JBoss Drools meets Hibernate</title>
		<link>http://www.redhatmagazine.com/2008/07/11/jboss-drools-meets-hibernate/</link>
		<comments>http://www.redhatmagazine.com/2008/07/11/jboss-drools-meets-hibernate/#comments</comments>
		<pubDate>Fri, 11 Jul 2008 20:31:36 +0000</pubDate>
		<dc:creator>Jaroslaw Kijanowski</dc:creator>
		
		<category>technical</category>

		<category>JBoss</category>

		<guid isPermaLink="false">http://www.redhatmagazine.com/2008/07/11/jboss-drools-meets-hibernate/</guid>
		<description><![CDATA[Jaroslaw is a JBoss QA Engineer based in Poland, and recently published an introduction to Drools that he kindly shared with us. This second piece covers Drools (or JBoss Rules), the open source business rules engine&#8230; in this case combining it with Hibernate.  
This article is presented here in its entirety (with a trackback). [...]]]></description>
			<content:encoded><![CDATA[<p><em>Jaroslaw is a JBoss QA Engineer based in Poland, and recently published an <a href="http://www.kijanowski.eu/index.php?site=articles&#038;article=introduction&#038;lang=en">introduction to Drools</a> that he kindly shared with us. This second piece covers <a href="http://en.wikipedia.org/wiki/Drools">Drools (or JBoss Rules)</a>, the open source business rules engine&#8230; in this case combining it with <a href="http://en.wikipedia.org/wiki/Hibernate_%28Java%29">Hibernate</a>.  </p>
<p>This article is presented here in its entirety (with a trackback).  The original can be found on <a href="http://www.kijanowski.eu/index.php?site=articles&#038;article=drools_hibernate&#038;lang=en">Jaroslaw&#8217;s site</a>.  This article is also available in <a href="http://www.kijanowski.eu/index.php?site=articles&#038;article=drools_hibernate&#038;lang=de">German</a> and <a href="http://www.kijanowski.eu/index.php?site=articles&#038;article=drools_hibernate&#038;lang=pl">Polish</a>.</em></p>
<h2>Justification:</h2>
<p>Drools evaluates facts which are present in the working memory. But could it also reason over data stored in a relational database? This feature would extend Drools&#8217; range of applicability and since this is an often asked question in the mailing list, it&#8217;s worth to know the answer which sounds: &#8220;of course Drools can!&#8221;</p>
<h2>Abstract:</h2>
<p>Hibernate, one of the most favorite <a href="http://en.wikipedia.org/wiki/Object-relational_mapping">ORM</a> tools, allows to handle data stored in a relational database. This article will describe how one can access a Hibernate session from inside the rule engine. I will use PostgreSQL as a data source. Besides that I will create two classes, Game and Player, having a many-to-many relationship.<a id="more-966"></a></p>
<ol>
<li><a href="#newproject">Creating a new Drools project</a></li>
<li><a href="#classes">Class files</a></li>
<li><a href="#hibernate">Hibernate</a></li>
<li><a href="#drools">Drools</a></li>
<li><a href="#howdoesitwork">How does it work?</a></li>
<li><a href="#final">Results</a>
<li><a href="#summary">Summary</a></li>
</ol>
<h2><a name="newproject">Creating a new Drools project</a></h2>
<p><i>You still don&#8217;t have installed the Drools 4.0.7 Eclipse Workbench? Just have a look at this article: <a href="http://www.kijanowski.eu/index.php?site=articles&#038;article=introduction&#038;lang=en">&#8220;Rules fall from your eyes&#8221;</a>.</i></p>
<p>Menu File -> New -> Project -> Drools - > Rule Project -> Next. Provide the project&#8217;s name, drools-hibernate-demo, and click on Next, to <b>uncheck</b> the example files</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2658892369/" title="fig2-1_step1 by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3096/2658892369_0eea61eb8e.jpg" width="500" height="458" alt="fig2-1_step1" /></a></p>
<h2><a name="classes">Class files</a></h2>
<p>Create a package &#8220;eu.kijanowski.drools.hibernate&#8221; in the src/main/java directory. We will use two class files, Player.java and Game.java. They have a many-tom-many relationship, since one player can own many games and one game can be owned by many players ;)</p>
<p><i>Class Game.java</i></p>
<pre>
package eu.kijanowski.drools.hibernate;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

public class Game implements Serializable {
	private static final long serialVersionUID = 1L;
	private Long id = null;
	private String name;
	private double price;
	private int levels;
	private Set&lt;Player&gt; players = new HashSet&lt;Player&gt;();

	public Game() {
	}

	public Game(String name, double price, int levels) {
		this.name = name;
		this.price = price;
		this.levels = levels;
	}

	public Long getId() {
		return id;
	}

	@SuppressWarnings("unused")
	private void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public double getPrice() {
		return price;
	}

	public void setPrice(double price) {
		this.price = price;
	}

	public int getLevels() {
		return levels;
	}

	public void setLevels(int levels) {
		this.levels = levels;
	}

	public Set&lt;Player&gt; getPlayers() {
		return players;
	}

	public void setPlayers(Set&lt;Player&gt; players) {
		this.players = players;
	}

	public String toString() {
		return name + " with " + levels + " levels for " + price;
	}
}
</pre>
<p><i>Class Player.java</i></p>
<pre>
package eu.kijanowski.drools.hibernate;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

public class Player implements Serializable {
	private static final long serialVersionUID = 1L;
	private Long id;
	private String name;
	private int age;
	private Set&lt;Game&gt; games = new HashSet&lt;Game&gt;();

	public Player() {
	}

	public Player(String name, int age, HashSet&lt;Game&gt; games) {
		this.name = name;
		this.age = age;
		this.games = games;
	}

	public Long getId() {
		return id;
	}

	@SuppressWarnings("unused")
	private void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Set&lt;Game&gt; getGames() {
		return games;
	}

	public void setGames(Set&lt;Game&gt; games) {
		this.games = games;
	}

	public String toString() {
		return name;
	}
}	
</pre>
<h2><a name="hibernate">Hibernate</a></h2>
<p>Without going into details, <a href="http://www.hibernate.org/6.html">download the binaries</a>, Hibernate Core 3.2.6 and extract the archive.</p>
<p>In our project create a directory called lib and copy over following files:</p>
<ul>
<li>hibernate-3.2.6.GA/lib/antlr-2.7.6.jar</li>
<li>hibernate-3.2.6.GA/lib/asm.jar</li>
<li>hibernate-3.2.6.GA/lib/cglib-2.1.3.jar</li>
<li>hibernate-3.2.6.GA/lib/commons-collections-2.1.1.jar</li>
<li>hibernate-3.2.6.GA/lib/commons-logging-1.0.4.jar</li>
<li>hibernate-3.2.6.GA/lib/dom4j-1.6.1.jar</li>
<li>hibernate-3.2.6.GA/hibernate3.jar</li>
<li>hibernate-3.2.6.GA/lib/jta.jar</li>
</ul>
<p>Now we have to add these libraries to the &#8220;build path&#8221;: from the menu choose Project -&gt; Properties -&gt; Java Build Path -&gt; Libraries and	then Add External JARs and navigate to the project&#8217;s lib directory.	Mark all files and click OK.</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2658892523/" title="fig2-2_libs by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3276/2658892523_dd6937c6a2.jpg" width="470" height="500" alt="fig2-2_libs" /></a></p>
<p>It&#8217;s time to configure Hibernate. In the src/main/java directory create a file called hibernate.cfg.xml:</p>
<pre>
&lt;?xml version='1.0' encoding='utf-8'?&gt;
&lt;!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"&gt;
 &lt;hibernate-configuration&gt;

     &lt;session-factory&gt;
         &lt;property name="connection.driver_class"&gt;org.postgresql.Driver&lt;/property&gt;
         &lt;property name="connection.url"&gt;jdbc:postgresql://localhost/droolsdb&lt;/property&gt;
         &lt;property name="connection.username"&gt;droolsuser&lt;/property&gt;

         &lt;property name="connection.password"&gt;&lt;/property&gt;
         &lt;property name="connection.pool_size"&gt;1&lt;/property&gt;
         &lt;property name="dialect"&gt;org.hibernate.dialect.PostgreSQLDialect&lt;/property&gt;
         &lt;property name="transaction.factory_class"&gt;org.hibernate.transaction.JDBCTransactionFactory&lt;/property&gt;

         &lt;property name="current_session_context_class"&gt;thread&lt;/property&gt;
         &lt;property name="cache.provider_class"&gt;org.hibernate.cache.NoCacheProvider&lt;/property&gt;
         &lt;property name="show_sql"&gt;true&lt;/property&gt;
         &lt;property name="hbm2ddl.auto"&gt;create&lt;/property&gt;

         &lt;mapping resource="eu/kijanowski/drools/hibernate/model.hbm.xml"/&gt;
     &lt;/session-factory&gt;
 &lt;/hibernate-configuration&gt;
 </pre>
<p>As you see, we&#8217;re going to use the org.postgresql.Driver class,	hence we need to add another library to our project. From the <a href="http://jdbc.postgresql.org/download.html">PostgreSQL home	page</a> we can download the required driver. I&#8217;m using <a href="http://jdbc.postgresql.org/download/postgresql-8.2-508.jdbc3.jar">8.2-508	JDBC 3</a>. Don&#8217;t forget to add it to the Java Build Path, like we did with the hibernate library.</p>
<p>It&#8217;s time to <a	href="http://www.postgresql.org/ftp/source/v8.2.9/">download</a> and start PostgreSQL and create a user and schema.</p>
<pre>
/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data&gt;logfile 2&gt;&#038;1 &#038;
/usr/local/pgsql/bin/createuser -h 127.0.0.1 -p 5432 -U postgres -s -d -R droolsuser
/usr/local/pgsql/bin/createdb -h 127.0.0.1 -p 5432 -U postgres -O droolsuser droolsdb
</pre>
<p>The last step is mapping - this is connecting the world of Java	objects with the world of relations (tabels). Create model.hbm.xml in the eu.kijanowski.drools.hibernate package:</p>
<pre>
&lt;?xml version='1.0' encoding='utf-8'?&gt;
&lt;!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"&gt;
&lt;hibernate-mapping package="eu.kijanowski.drools.hibernate"&gt;
	&lt;class name="Player" table="PLAYERS"&gt;
		&lt;id name="id" column="PLAYER_ID" type="java.lang.Long"&gt;

			&lt;generator class="sequence"&gt;
				&lt;param name="sequence"&gt;player_id_seq&lt;/param&gt;
			&lt;/generator&gt;
		&lt;/id&gt;
		&lt;property name="name" column="NAME" length="10" not-null="true"
			type="java.lang.String" /&gt;

		&lt;property name="age" column="AGE" not-null="true"
			type="java.lang.Integer" /&gt;

		&lt;set name="games" table="PLAYERS_GAMES" cascade="persist, delete, merge, save-update"&gt;
			&lt;key column="PLAYER_ID" /&gt;
			&lt;many-to-many column="GAME_ID" class="Game" /&gt;
		&lt;/set&gt;

	&lt;/class&gt;

	&lt;class name="Game" table="GAMES"&gt;
		&lt;id name="id" column="GAME_ID" type="java.lang.Long"&gt;
			&lt;generator class="sequence"&gt;
				&lt;param name="sequence"&gt;game_id_seq&lt;/param&gt;
			&lt;/generator&gt;

		&lt;/id&gt;
		&lt;property name="name" column="NAME" length="10" not-null="true"
			type="java.lang.String" /&gt;
		&lt;property name="price" column="PRICE" not-null="true"
			type="java.lang.Double" /&gt;
		&lt;property name="levels" column="LEVELS" not-null="true"
			type="java.lang.Integer" /&gt;
		&lt;set name="players" table="PLAYERS_GAMES"&gt;
			&lt;key column="GAME_ID" /&gt;

			&lt;many-to-many column="PLAYER_ID" class="Player"/&gt;
		&lt;/set&gt;

	&lt;/class&gt;
&lt;/hibernate-mapping&gt;
</pre>
<h2><a name="drools">Drools</a></h2>
<p>We&#8217;re almost there. We olny have to create the rule file and a testing class which will start our example. But how are we going to use Hibernate in our application? Well, in our testing class we will create a Hibernate session and provide it to the rule engine&#8217;s working memory as a constant global value. This allows us to access the database from inside the rule file. Next we will create a query and access the result&#8217;s objects using the keyword &#8220;from&#8221;. Let&#8217;s do this:</p>
<p><i>Class DroolsHibernateTest.java</i></p>
<pre>
package eu.kijanowski.drools.hibernate;

import java.io.InputStreamReader;
import java.io.Reader;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import org.drools.RuleBase;
import org.drools.RuleBaseFactory;
import org.drools.WorkingMemory;
import org.drools.compiler.PackageBuilder;
import org.drools.rule.Package;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class DroolsHibernateTest {

	private final static SessionFactory factory;
	static {
		Configuration cfg = new Configuration().configure();
		factory = cfg.buildSessionFactory();
	}

	public static final void main(String[] args) {
		try {

			Game g1 = new Game("BF2", 39.99, 5);
			Game g2 = new Game("AoE3", 129.99, 45);
			Game g3 = new Game("BF3", 139.99, 15);

			HashSet&lt;Game&gt; games1 = new HashSet&lt;Game&gt;();
			games1.add(g1);
			games1.add(g2);
			games1.add(g3);
			HashSet&lt;Game&gt; games2 = new HashSet&lt;Game&gt;();
			games2.add(g1);
			games2.add(g3);

			Player p1 = new Player("jarek", 26, games1);
			Player p2 = new Player("ewelina", 25, games2);

			System.out.println(p1.getGames());
			System.out.println(p2.getGames());

			/* open a Hibernate session and persist data */
			Session session = factory.openSession();
			Transaction tx = session.beginTransaction();
			session.save(p1);
			session.save(p2);
			tx.commit();
			session.close();

			/* Let's verify the persisted data */
			session = factory.openSession();
			tx = session.beginTransaction();

			List&lt;Player&gt; players = session.createCriteria(Player.class).list();
			System.out.println(players.size() + " player(s) found:");
			for (Iterator&lt;Player&gt; iter = players.iterator(); iter.hasNext();) {
				Player player = (Player) iter.next();
				System.out.println(player.getName() + " has following games: "
						+ player.getGames());
			}
			tx.commit();
			session.close();

			// load up the rulebase
			RuleBase ruleBase = readRule();
			WorkingMemory workingMemory = ruleBase.newStatefulSession();

			/* pass a hibernate session to the working memory as a global */
			session = factory.openSession();
			workingMemory.setGlobal("hibernateSession", session);

			workingMemory.fireAllRules();
			session.close();

		} catch (Throwable t) {
			t.printStackTrace();
		}
	}

	private static RuleBase readRule() throws Exception {
		// read in the source
		Reader source = new InputStreamReader(DroolsHibernateTest.class
				.getResourceAsStream("/Demo.drl"));

		PackageBuilder builder = new PackageBuilder();

		// this will parse and compile in one step
		builder.addPackageFromDrl(source);

		// get the compiled package (which is serializable)
		Package pkg = builder.getPackage();

		// add the package to a rulebase (deploy the rule package).
		RuleBase ruleBase = RuleBaseFactory.newRuleBase();
		ruleBase.addPackage(pkg);
		return ruleBase;
	}

}
</pre>
<p>The rule file can be created using the drools plug-in. Just click on the src/main/rules directory and from the menu File -&gt; New -&gt; Other&#8230; click Drools -&gt; Rule Resource:</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2658892635/" title="fig2-3_demodrl by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3189/2658892635_58deebe2a6.jpg" width="414" height="500" alt="fig2-3_demodrl" /></a></p>
<p><i>Demo.drl</i></p>
<pre>
package eu.kijanowski.drools.hibernate

global org.hibernate.Session hibernateSession;

rule "hibernate_from"
	when
		game:Game() from hibernateSession.createQuery("select games from Player p where p.age >= :age").setProperties( {"age" : 18 }).list()
	then
		System.out.println("The game "+game.getName() +"is owned by "+game.getPlayers());
end
</pre>
<h2><a name="howdoesitwork">How does it work?</a></h2>
<p>Let&#8217;s have a look at the <b>global</b> keyword in the rule file. This allows us to provide the rule engine with constants and variables from outside. Potential usecases are:</p>
<ul>
<li>return results back to the application
<li>
<li>access services, like Hibernate, JMS, file writers, email senders, etc.</li>
</ul>
<p>What&#8217;s worth to know is that you should <b>never change a global</b>, when you use it in the condition part (LHS) of a rule. Moreover globals are <b>not designed to share any data</b> between rules, this can be done via facts living in the working memory.</p>
<p>The condition element <b>from</b> allows to access data from other sources than the working memory. These can be Collectins, Maps and results coming from called methods. In our scenario we&#8217;ve created a query and received a list of games.</p>
<h2><a name="final">Results</a></h2>
<p>After running our example (Run as -&gt; Java Application) the console will show:</p>
<pre>
Jun 14, 2008 8:58:11 AM org.hibernate.cfg.Environment &lt;clinit&gt;

INFO: Hibernate 3.2.6
Jun 14, 2008 8:58:11 AM org.hibernate.cfg.Environment &lt;clinit&gt;
INFO: hibernate.properties not found
Jun 14, 2008 8:58:11 AM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: Bytecode provider name : cglib
Jun 14, 2008 8:58:11 AM org.hibernate.cfg.Environment &lt;clinit&gt;
INFO: using JDK 1.4 java.sql.Timestamp handling
Jun 14, 2008 8:58:11 AM org.hibernate.cfg.Configuration configure
INFO: configuring from resource: /hibernate.cfg.xml
Jun 14, 2008 8:58:11 AM org.hibernate.cfg.Configuration getConfigurationInputStream
INFO: Configuration resource: /hibernate.cfg.xml
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.Configuration addResource
INFO: Reading mappings from resource : eu/kijanowski/drools/hibernate/model.hbm.xml
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.HbmBinder bindRootPersistentClassCommonValues
INFO: Mapping class: eu.kijanowski.drools.hibernate.Player -&gt; PLAYERS
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.HbmBinder bindCollection
INFO: Mapping collection: eu.kijanowski.drools.hibernate.Player.games -&gt; PLAYERS_GAMES
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.HbmBinder bindRootPersistentClassCommonValues
INFO: Mapping class: eu.kijanowski.drools.hibernate.Game -&gt; GAMES
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.HbmBinder bindCollection
INFO: Mapping collection: eu.kijanowski.drools.hibernate.Game.players -&gt; PLAYERS_GAMES
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.Configuration doConfigure
INFO: Configured SessionFactory: null
Jun 14, 2008 8:58:12 AM org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: Using Hibernate built-in connection pool (not for production use!)
Jun 14, 2008 8:58:12 AM org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: Hibernate connection pool size: 1
Jun 14, 2008 8:58:12 AM org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: autocommit mode: false
Jun 14, 2008 8:58:12 AM org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: using driver: org.postgresql.Driver at URL: jdbc:postgresql://localhost/droolsdb
Jun 14, 2008 8:58:12 AM org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: connection properties: {user=droolsuser, password=****}
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: RDBMS: PostgreSQL, version: 8.2.5
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: JDBC driver: PostgreSQL Native Driver, version: PostgreSQL 8.2 JDBC3 with SSL (build 508)
Jun 14, 2008 8:58:12 AM org.hibernate.dialect.Dialect &lt;init&gt;

INFO: Using dialect: org.hibernate.dialect.PostgreSQLDialect
Jun 14, 2008 8:58:12 AM org.hibernate.transaction.TransactionFactoryFactory buildTransactionFactory
INFO: Transaction strategy: org.hibernate.transaction.JDBCTransactionFactory
Jun 14, 2008 8:58:12 AM org.hibernate.transaction.TransactionManagerLookupFactory getTransactionManagerLookup
INFO: No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended)
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Automatic flush during beforeCompletion(): disabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Automatic session close at end of transaction: disabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: JDBC batch size: 15
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: JDBC batch updates for versioned data: disabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Scrollable result sets: enabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: JDBC3 getGeneratedKeys(): disabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Connection release mode: auto
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Default batch fetch size: 1
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Generate SQL with comments: disabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Order SQL updates by primary key: disabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Order SQL inserts for batching: disabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory createQueryTranslatorFactory
INFO: Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory
Jun 14, 2008 8:58:12 AM org.hibernate.hql.ast.ASTQueryTranslatorFactory &lt;init&gt;
INFO: Using ASTQueryTranslatorFactory
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Query language substitutions: {}
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: JPA-QL strict compliance: disabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Second-level cache: enabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Query cache: disabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory createCacheProvider
INFO: Cache provider: org.hibernate.cache.NoCacheProvider
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Optimize cache for minimal puts: disabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Structured second-level cache entries: disabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Echoing all SQL to stdout
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Statistics: disabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Deleted entity synthetic identifier rollback: disabled
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Default entity-mode: pojo
Jun 14, 2008 8:58:12 AM org.hibernate.cfg.SettingsFactory buildSettings
INFO: Named query checking : enabled
Jun 14, 2008 8:58:12 AM org.hibernate.impl.SessionFactoryImpl &lt;init&gt;
INFO: building session factory
Jun 14, 2008 8:58:12 AM org.hibernate.impl.SessionFactoryObjectFactory addInstance
INFO: Not binding factory to JNDI, no JNDI name configured
Jun 14, 2008 8:58:12 AM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: Running hbm2ddl schema export
Jun 14, 2008 8:58:12 AM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: exporting generated schema to database
Jun 14, 2008 8:58:13 AM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: schema export complete
[BF3 with 15 levels for 139.99, BF2 with 5 levels for 39.99, AoE3 with 45 levels for 129.99]
[BF3 with 15 levels for 139.99, BF2 with 5 levels for 39.99]
Hibernate: select nextval ('player_id_seq')
Hibernate: select nextval ('game_id_seq')
Hibernate: select nextval ('game_id_seq')
Hibernate: select nextval ('game_id_seq')
Hibernate: select nextval ('player_id_seq')
Hibernate: insert into PLAYERS (NAME, AGE, PLAYER_ID) values (?, ?, ?)
Hibernate: insert into GAMES (NAME, PRICE, LEVELS, GAME_ID) values (?, ?, ?, ?)
Hibernate: insert into GAMES (NAME, PRICE, LEVELS, GAME_ID) values (?, ?, ?, ?)
Hibernate: insert into GAMES (NAME, PRICE, LEVELS, GAME_ID) values (?, ?, ?, ?)
Hibernate: insert into PLAYERS (NAME, AGE, PLAYER_ID) values (?, ?, ?)
Hibernate: insert into PLAYERS_GAMES (PLAYER_ID, GAME_ID) values (?, ?)
Hibernate: insert into PLAYERS_GAMES (PLAYER_ID, GAME_ID) values (?, ?)
Hibernate: insert into PLAYERS_GAMES (PLAYER_ID, GAME_ID) values (?, ?)
Hibernate: insert into PLAYERS_GAMES (PLAYER_ID, GAME_ID) values (?, ?)
Hibernate: insert into PLAYERS_GAMES (PLAYER_ID, GAME_ID) values (?, ?)
Hibernate: select this_.PLAYER_ID as PLAYER1_0_0_, this_.NAME as NAME0_0_, this_.AGE as AGE0_0_ from PLAYERS this_
2 player(s) found:
Hibernate: select games0_.PLAYER_ID as PLAYER1_1_, games0_.GAME_ID as GAME2_1_, game1_.GAME_ID as GAME1_2_0_, game1_.NAME as NAME2_0_, game1_.PRICE as PRICE2_0_, game1_.LEVELS as LEVELS2_0_ from PLAYERS_GAMES games0_ left outer join GAMES game1_ on games0_.GAME_ID=game1_.GAME_ID where games0_.PLAYER_ID=?
jarek has following games: [AoE3 with 45 levels for 129.99, BF3 with 15 levels for 139.99, BF2 with 5 levels for 39.99]
Hibernate: select games0_.PLAYER_ID as PLAYER1_1_, games0_.GAME_ID as GAME2_1_, game1_.GAME_ID as GAME1_2_0_, game1_.NAME as NAME2_0_, game1_.PRICE as PRICE2_0_, game1_.LEVELS as LEVELS2_0_ from PLAYERS_GAMES games0_ left outer join GAMES game1_ on games0_.GAME_ID=game1_.GAME_ID where games0_.PLAYER_ID=?
ewelina has following games: [BF3 with 15 levels for 139.99, BF2 with 5 levels for 39.99]
Hibernate: select game2_.GAME_ID as GAME1_2_, game2_.NAME as NAME2_, game2_.PRICE as PRICE2_, game2_.LEVELS as LEVELS2_ from PLAYERS player0_ inner join PLAYERS_GAMES games1_ on player0_.PLAYER_ID=games1_.PLAYER_ID inner join GAMES game2_ on games1_.GAME_ID=game2_.GAME_ID where player0_.AGE&gt;=?
Hibernate: select players0_.GAME_ID as GAME2_1_, players0_.PLAYER_ID as PLAYER1_1_, player1_.PLAYER_ID as PLAYER1_0_0_, player1_.NAME as NAME0_0_, player1_.AGE as AGE0_0_ from PLAYERS_GAMES players0_ left outer join PLAYERS player1_ on players0_.PLAYER_ID=player1_.PLAYER_ID where players0_.GAME_ID=?
The game BF2is owned by [jarek, ewelina]
Hibernate: select players0_.GAME_ID as GAME2_1_, players0_.PLAYER_ID as PLAYER1_1_, player1_.PLAYER_ID as PLAYER1_0_0_, player1_.NAME as NAME0_0_, player1_.AGE as AGE0_0_ from PLAYERS_GAMES players0_ left outer join PLAYERS player1_ on players0_.PLAYER_ID=player1_.PLAYER_ID where players0_.GAME_ID=?
The game BF3is owned by [jarek, ewelina]
Hibernate: select players0_.GAME_ID as GAME2_1_, players0_.PLAYER_ID as PLAYER1_1_, player1_.PLAYER_ID as PLAYER1_0_0_, player1_.NAME as NAME0_0_, player1_.AGE as AGE0_0_ from PLAYERS_GAMES players0_ left outer join PLAYERS player1_ on players0_.PLAYER_ID=player1_.PLAYER_ID where players0_.GAME_ID=?
The game AoE3is owned by [jarek]
The game BF2is owned by [jarek, ewelina]
The game BF3is owned by [jarek, ewelina]
</pre>
<h2><a name="summary">Summary</a></h2>
<p>This article shows how you can easily integrate Hibernate with the Drools rule engine. This allows you to reason over data, which does not only live in the working memory, but also in an external database.</p>
<div class="caption">All images and content copyright © 2008 Jaroslaw Kianowski. All rights reserved. Used with permission.</div>
<h2>Related articles</h2>
<ul>
<li><a href="http://www.redhatmagazine.com/2008/03/27/video-mark-proctor/">Video: Mark Proctor and JBoss Drools</a></li>
<li><a href="http://www.redhatmagazine.com/2008/05/01/video-mark-proctor-part-2-origins-and-uses-of-jboss-drools/">Video: Mark Proctor, part 2: Origins and uses of JBoss Drools</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.redhatmagazine.com/2008/07/11/jboss-drools-meets-hibernate/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Introduction to Drools:  Rules fall from your eyes</title>
		<link>http://www.redhatmagazine.com/2008/07/10/introduction-to-drools-rules-fall-from-your-eyes/</link>
		<comments>http://www.redhatmagazine.com/2008/07/10/introduction-to-drools-rules-fall-from-your-eyes/#comments</comments>
		<pubDate>Thu, 10 Jul 2008 22:25:55 +0000</pubDate>
		<dc:creator>Jaroslaw Kijanowski</dc:creator>
		
		<category>technical</category>

		<category>JBoss</category>

		<guid isPermaLink="false">http://www.redhatmagazine.com/2008/07/10/introduction-to-drools-rules-fall-from-your-eyes/</guid>
		<description><![CDATA[One of our long-time writers introduced us to Jaroslaw, a JBoss QA Engineer based in Poland, and mentioned that he&#8217;d been working on some documentation we might find interesting.  And, boy, do we!  This first piece de-mystifies the complex world of rules engines.  Whether you call it Drools, or JBoss Rules, or [...]]]></description>
			<content:encoded><![CDATA[<p><em>One of our long-time writers introduced us to Jaroslaw, a JBoss QA Engineer based in Poland, and mentioned that he&#8217;d been working on some documentation we might find interesting.  And, boy, do we!  This first piece de-mystifies the complex world of rules engines.  Whether you call it <a href="http://en.wikipedia.org/wiki/Drools">Drools</a>, or <a href="http://www.jboss.com/products/rules">JBoss Rules</a>, or JBoss Drools&#8230;  you still might want to know what it does and how it functions in your technical environment. (Not to mention that it&#8217;s just all kinds of logical and interesting&#8211;if you like brainteaser word puzzles or abstract math questions, this qualifies as pleasure reading.)</p>
<p>This article is presented here in its entirety (with a trackback).  The original can be found on <a href="http://www.kijanowski.eu/index.php?site=articles&#038;article=introduction&#038;lang=en">Jaroslaw&#8217;s site</a>.  This article is also available in <a href="http://www.kijanowski.eu/index.php?site=articles&#038;article=introduction&#038;lang=de">German</a> and <a href="http://www.kijanowski.eu/index.php?site=articles&#038;article=introduction&#038;lang=pl">Polish</a>.</em></em></p>
<h2>Justification:</h2>
<p>Either you&#8217;re a developer, architect or a business analyst, it&#8217;s worth to get familiar at least with the first chapter of this article. It contains an introduction into the world of rule engines, which increase the readability of certain applications and make them easier to manage and to maintain. It applies to apps that take decisions, which depend on events or a state of some objects.</p>
<h2>Abstract:</h2>
<p>This article contains an introduction to rule engines, a description of an installation of Eclipse IDE and a guide how to configure Eclipse with the Drools Workbench plug-in.<a id="more-968"></a></p>
<ol>
<li><a href="#ruleengine">A Rule Engine</a></li>
<li><a href="#drools">Drools</a></li>
<li><a href="#advantages">Advantages of the Drools rule engine</a></li>
<li><a href="#brms">BRMS</a></li>
<li><a href="#productionsvsevents">Production rules vs. events</a></li>
<li><a href="#start">Let&#8217;s get started!</a>
<li><a href="#howdoesitwork">What happened?</a></li>
<li><a href="#summary">Summary</a></li>
</ol>
<h2><a name="ruleengine">A Rule Engine</a></h2>
<p>A <a href="http://en.wikipedia.org/wiki/Rules_engine" target="_blank">rule engine</a> is a piece of software, which having some knowledge is able to perform conclusions. Knowledge and inferences are stored in rules, which are called <a href="http://en.wikipedia.org/wiki/Production_rule_system" target="_blank">production rules</a>. In other words, production rules consist of conditions and actions, which are executed when their	conditions are true. Besides that there&#8217;s a <a href="http://en.wikipedia.org/wiki/Working_memory" target="_blank">working memory</a>, which stores all objects we may want to use while making a decision. The state of this memory can vary, because actions can add,	delete or change existing objects. Moreover rules can get into a conflict. This happens when there are more than one rule which are true, at the same time. Then conflict resolution is provided by the agenda. It arranges the order of actions, which has been selected to be run. The agenda is an important part of a rule engine, because the execution of an action may invalidate a rule and in consequence cancel another action. To sum it up, a rule engine can be presented as follows:</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2658891787/" title="fig1-1_rule_engine_en by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3268/2658891787_bb60abfca1.jpg" width="346" height="500" alt="fig1-1_rule_engine_en" /></a></p>
<p>Rule engines perfectly fits to:</p>
<ul>
<li>calculate a discount of orders based on the amount or price</li>
<li>determine an insurance policy dependent on the age of the driver and the value of the car</li>
<li>make a diagnosis</li>
<li>detect imminence of failure</li>
<li>solve riddles</li>
</ul>
<h2><a name="drools">Drools</a></h2>
<p>One of the open source implementations of a rule engine is <a href="http://www.jboss.org/drools/" target="_blank">Drools</a>. It&#8217;s a Java library distributed under the <a href="http://www.apache.org/licenses/LICENSE-2.0.html" target="_blank">Apache license</a>. It&#8217;s available as as a binary and a source download. You can get it and use in your applications for free.</p>
<p>The heart of a rule engine is an <a href="http://en.wikipedia.org/wiki/Inference_engine" target="_blank">inference engine</a>. Its job is to match facts and rules (pattern matching) to make conclusions which ends in running actions. This matching is done by the <a href="http://en.wikipedia.org/wiki/Rete_algorithm" target="_blank">RETE</a> algorithm, which has been extended by object oriented concepts.</p>
<p>As I already mentioned, <b>knowledge</b> is presented in form of production rules. <a href="http://en.wikipedia.org/wiki/Knowledge_representation" target="_blank">Knowledge representation</a>, which is a way to provide the rule engine with data, is based on <a href="http://en.wikipedia.org/wiki/First-order_logic" target="_blank">FOL - first order logic</a>. FOL allows to evaluate expressions like 2 + 3 == 5 or customer.age > 17.</p>
<p>The two main parts of a production rule are its condition(s) and action(s):</p>
<pre>
when
	Customer (age > 17)
	then
	System.out.println("Customer is full age");
</pre>
<p>The rule above is going to evaluated as true for all those facts, which represents full age customers. A similar sql query would look as follows:</p>
<p><code>SELECT * from Customers c WHERE c.age > 17</code></p>
<p>There are two ways to determine the truth of a rule (condition)	and run its action in consequence. <a href="http://en.wikipedia.org/wiki/Forward_chaining" target="_blank">Forward chaining</a> is driven with facts. Rules are checked, which <b>conditions</b> are true for facts being in the working memory. The process of pattern matching finishes, when there are no more rules left, whose conditions are true. <a href="http://en.wikipedia.org/wiki/Backward_chaining" target="_blank">Backward chaining</a> on the other hand is a goal<br />
driven approach. Only those rules are checked, whose <b>actions</b> can be matched with a goal. If that is the case, conditions of that rule are evaluated.</p>
<h2><a name="advantages">Advantages of the Drools rule engine</a></h2>
<ul>
<li>separates your <b>application</b> from <b>conditions</b> which control the flow
<ul>
<li>rules are stored in separate files</li>
<li>rules can be modified by different groups of people</li>
<li>changing rules does not require to recompile or to redeploy	the whole application</li>
<li>putting all rules into one place makes it easier to control or manage the flow of the application</li>
</ul>
</li>
<li>rules can replace <b>complex</b> if - then statements in an <b>easy	and readable</b> way
<ul>
<li>the rule language is not very difficult to learn</li>
<li>a plug-in for Eclipse IDE makes it easier to write rules and allows to have a look at the RETE tree</li>
</ul>
</li>
<li>problems are not solved using a <b>complicated algorithm</b>, but via rules, which are <b>easier to read and understand</b> than<br />
		code</p>
<ul>
<li>with <a href="http://en.wikipedia.org/wiki/Declarative_programming"	target="_blank">declarative programming</a> you are focused on <b>WHAT</b> you are solving, and not <b>HOW</b></li>
<li>a group of problems can be easily solved with rules, where the algorithmic approach may be unusable (too complex, too time consuming, etc)</li>
</ul>
</li>
<li>access to the reasoning process allows you to <b>understand	why</b> a particular decisions has been made</li>
</ul>
<p>Summing up a rule engine should be considered when the solution	of a problem seems complex or simply the algorithm contains a lot of it - then or switch statements. Rules should be also taken into account, when logic that drives your application often changes or has to be	managed by people who don&#8217;t (or shouldn&#8217;t) have access to the application itself (to recompile/redeploy it).</p>
<h2><a name="brms">BRMS</a></h2>
<p>Drools is not just a rule engine. It provides also an application for managing rules, called Business Rules Management System. It allows to create, modify, delete, branch and persist rules. Moreover it&#8217;s possible to assign roles to users and a login mechanism	and LDAP integration makes it easy to introduce security. So for example, a business analyst may have access to modify a rule, a developer on the other hand may be able only to read it. A BRMS is a single point of truth.</p>
<h2><a name="productionsvsevents">Production rules vs. events</h2>
<p>Production rules consists mainly of conditions (when) and actions (then), e.g. when there&#8217;s a customer, who has a driving license for less than 5 years, then offer her an expensive insurance. Other type of rules are events (<a href="http://en.wikipedia.org/wiki/Event_Condition_Action" target="_blank">event condition action</a>). In that case a rule engine looks for event patterns to match registered events. An event may be selling the last item or a breakdown of traffic lights. Another	difference is the moment, when actions are executed. In the production rules environment, it&#8217;s the developer, who decides, when the rule engine is going to fire all actions. However, when working with events, an action is fired, when the associated event did happen.</p>
<h2><a name="start">Let&#8217;s get started!</a></h2>
<p>&#8230; and install an IDE. I&#8217;ve chosen Eclipse, because it&#8217;s good enough and Drools provides a plug-in for writing rules.</p>
<p><a href="http://www.eclipse.org/downloads/" target="_blank">Eclipse IDE</a>, Eclipse IDE for Java EE Developers (eclipse-jee-europa-winter-linux-gtk.tar.gz)</p>
<p>After you&#8217;ve downloaded it, extract and start Eclipse. From the	menu click on Help -&gt; Software Updates -&gt; Find and install&#8230; -&gt; Search for new features to install -&gt; New Remote Site&#8230;</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2658891901/" title="fig1-2_install1 by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3018/2658891901_a13a162530.jpg" width="470" height="500" alt="fig1-2_install1" /></a></p>
<p>You can find the URL on the Drools <a href="http://www.jboss.org/drools/downloads.html" target="_blank">downloads page</a> under &#8220;Drools IDE Update Site&#8221;. It&#8217;s a link to the plug-in. For Drools 4.0.7 Eclipse Workbench for Europa 3.3 it is <a href="http://downloads.jboss.com/drools/updatesite3.3/">http://downloads.jboss.com/drools/updatesite3.3/</a></p>
<p>We choose OK and Finish. Eclipse will show the search results:</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2658891991/" title="fig1-3_install2 by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3109/2658891991_1a0376f6a1.jpg" width="500" height="454" alt="fig1-3_install2" /></a></p>
<p>Click on Next, accept the license and choose Finish, to start to download the plug-in. After that Eclipse will show another window, where we click on &#8220;Install all&#8221;. When asked for a restart we click on &#8220;Yes&#8221;. To verify a successful installation of the plug-in, make sure a new Drools icon showed up in the toolbar.</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2658892115/" title="fig1-4_symbol by redhatmag, on Flickr"><img src="http://farm4.static.flickr.com/3097/2658892115_560094df70_o.png" width="275" height="81" alt="fig1-4_symbol" /></a></p>
<p>Let&#8217;s have a look at our first Drools application. Click on the Drools &#8220;head&#8221; and choose &#8220;New Rule Project&#8221;.</p>
<p><a href="http://www.flickr.com/photos/redhatmagazine/2658892229/" title="fig1-6_step0 by redhatmag, on Flickr"><img src="http://farm3.static.flickr.com/2312/2658892229_9e1f9cec61_o.png" width="358" height="203" alt="fig1-6_step0" /></a></p>
<p>Type a name for the project and click Finish. The Drools plug-in creates a standard layout and provides all the necessary libraries to get started. To run this application click on com.sample.DroolsTest and	press &#8220;Shift + Alt + X&#8221; and then &#8220;J&#8221;. The console will show th following:</p>
<pre>
Hello World
	Goodbye cruel world
</pre>
<h2><a name="howdoesitwork">What happened?</a></h2>
<p>This example has been described in the <a href="http://downloads.jboss.com/drools/docs/4.0.7.19894.GA/html_single/index.html#d0e5986" target="_blank">Drools documentation</a>. I&#8217;m not going into detail what&#8217;s happening in DroolsTest.java, where the rule engine is initialized and rules are read. The more interesting part is the rule file itself, Sample.drl.</p>
<p>This file has two rules. At the beginning, the working memory contains one object of the class Message, whose fields message and status are set to &#8220;Hello World&#8221; and &#8220;0&#8243;. When fireAllRules() is called, all actions are going to be executed. It&#8217;s worth to note that conditions of rules are evaluated <b>when an object is added into the working memory</b>. The condition of the rule called &#8220;Hello World&#8221; is true, because there is an object in the working memory, which is a Message and has a status equal to &#8220;0&#8243;. Hence actions of this rule are going to be run. As a result, the status and message fields of that Message object are going to be modified. The <b>update()</b> command forces the rule engine to evaluate its rules again. This time the second rule&#8217;s condition is true and appropriate actions are executed.</p>
<h2><a name="summary">Summary</a></h2>
<p>This short introduction into the world of rule engines and the chosen implementation Drools showed their main characteristics and some potential fields of use. To get more familiar with this software I would suggest to read the <a href="http://www.jboss.org/drools/documentation.html" target="_blank">Drools documentation</a>.</p>
<div class="caption">All images and content copyright © 2008 Jaroslaw Kianowski. All rights reserved. Used with permission.</div>
<h2>Related articles</h2>
<ul>
<li><a href="http://www.redhatmagazine.com/2008/03/27/video-mark-proctor/">Video: Mark Proctor and JBoss Drools</a></li>
<li><a href="http://www.redhatmagazine.com/2008/05/01/video-mark-proctor-part-2-origins-and-uses-of-jboss-drools/">Video: Mark Proctor, part 2: Origins and uses of JBoss Drools</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.redhatmagazine.com/2008/07/10/introduction-to-drools-rules-fall-from-your-eyes/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Red Hat Enterprise Linux 5 virtualization on HP DL585: AMD Barcelona with Rapid Virtualization Indexing</title>
		<link>http://www.redhatmagazine.com/2008/07/03/red-hat-enterprise-linux-5-virtualization-on-hp-dl585-amd-barcelona-with-rapid-virtualization-indexing/</link>
		<comments>http://www.redhatmagazine.com/2008/07/03/red-hat-enterprise-linux-5-virtualization-on-hp-dl585-amd-barcelona-with-rapid-virtualization-indexing/#comments</comments>
		<pubDate>Thu, 03 Jul 2008 16:18:17 +0000</pubDate>
		<dc:creator>Sanjay Rao</dc:creator>
		
		<category>Red Hat Enterprise Linux</category>

		<category>technical</category>

		<guid isPermaLink="false">http://www.redhatmagazine.com/2008/07/03/red-hat-enterprise-linux-5-virtualization-on-hp-dl585-amd-barcelona-with-rapid-virtualization-indexing/</guid>
		<description><![CDATA[This article is a follow-up to Red Hat Enterprise Linux 5.1 utilizes nested paging on AMD Barcelona Processor to improve performance of virtualized guests.
With new hardware releases, customers are faced with situations in which they want to take advantage of increased speeds but are forced to stay on older hardware because their operating environments are [...]]]></description>
			<content:encoded><![CDATA[<p><em>This article is a follow-up to <a href="http://www.redhatmagazine.com/2007/11/20/red-hat-enterprise-linux-51-utilizes-nested-paging-on-amd-barcelona-processor-to-improve-performance-of-virtualized-guests/">Red Hat Enterprise Linux 5.1 utilizes nested paging on AMD Barcelona Processor to improve performance of virtualized guests</a>.</em></p>
<p>With new hardware releases, customers are faced with situations in which they want to take advantage of increased speeds but are forced to stay on older hardware because their operating environments are not supported on the newer hardware. Virtualizing their operating environment helps them get past this issue. Virtualization also helps them:</p>
<ul>
<li>Consolidate hardware to</li>
<ul>
<li>improve utilization</li>
<li>		reduce floor space requirements</li>
<li>		reduce power consumption</li>
</ul>
<li>Take advantage of hardware speed up without having to upgrade the software environment</li>
<li>Reduce downtime for upgrades</li>
<li>Create development and test environments</li>
</ul>
<p>RHEL 5 virtualization lets customers virtualize their existing systems and take advantage of the benefits mentioned above. <a id="more-955"></a></p>
<p>There are two modes to virtualize servers:</p>
<ul>
<li><strong>Para-virtualized servers.</strong> In this mode, the virtualized server has direct access to the hardware/hypervisor and delivers performance close to bare metal. Systems running Red Hat Enterprise Linux 4.5 and newer can be deployed as para-virtualized guests.</li>
<li><strong>Fully-virtualized servers.</strong> In this mode, the virtualized server interacts with the hypervisor through a hardware abstraction layer. The hypervisor presents its hardware as generic hardware through the abstraction layer so most operating systems can be run in a virtual mode on it. However, all the I/O, network, and memory requests from the guest have to be translated by the hypervisor. This translation results in very poor performance by systems deployed as fully virtualized guests. </li>
</ul>
<p>The I/O and network performance on fully virtualized guests can be improved by implementing para-virtualized drivers within these guests. </p>
<p>But memory access can still be an issue, particularly with process-based applications where processes need to modify virtual memory.  Specifically, the guest operating system (guest OS) in the virtual m