Templates
The templates project provides a framework for accepting some number of words (or terms) and producing search results from an LDAP. The most common use case being free form search input, like one would expect for a search engine.
A SearchTemplatesExecutor
is configured with an ordered number of SearchTemplates
. The first SearchTemplates
object is expected to format search filters for one term queries. The second SearchTemplates
object is expected to format search filters for two term queries, and so forth. The appropriate SearchTemplates
is selected based on the Query
that is provided to the executor and a search is executed for each of the filters in that templates. A single SearchResult
containing all results is returned. In this fashion a set of search terms can be transformed into LDAP search results.
Some named parameters are defined by the templates in order to write search filters:
{term1} is replaced with the first query term
{initial1} is replaced with the first letter of the first query term
{term2} is replaced with the second query term
{initial2} is replaced with the first letter of the second query term
…
{termN} is replaced with the Nth query term
{initialN} is replaced with the first letter of the Nth query term
SearchTemplates oneTermTemplate = new SearchTemplates (
"(|(telephoneNumber={term1})(localPhone={term1}))" ,
"(|(telephoneNumber=*{term1})(localPhone=*{term1}))" ,
"(|(givenName={term1})(sn={term1}))" ,
"(|(givenName={term1}*)(sn={term1}*))" ,
"(|(givenName=*{term1}*)(sn=*{term1}*))" );
SearchTemplates twoTermTemplate = new SearchTemplates (
"(&(givenName={term1})(sn={term2}))" ,
"(cn={term1} {term2})" ,
"(&(givenName={term1}*)(sn={term2}*))" ,
"(cn={term1}* {term2}*)" ,
"(&(givenName=*{term1}*)(sn=*{term2}*))" ,
"(cn=*{term1}* *{term2}*)" );
SearchTemplates threeTermTemplate = new SearchTemplates (
"(|(&(givenName={term1})(sn={term3}))(&(givenName={term2})(sn={term3})))" ,
"(|(cn={term1} {term2} {term3})(cn={term2} {term1} {term3}))" ,
"(|(&(givenName={term1}*)(sn={term3}*))(&(givenName={term2}*)(sn={term3}*)))" ,
"(|(cn={term1}* {term2}* {term3}*)(cn={term2}* {term1}* {term3}*))" ,
"(|(&(givenName=*{term1}*)(sn=*{term3}*))(&(givenName=*{term2}*)(sn=*{term3}*)))" ,
"(|(cn=*{term1}* *{term2}* *{term3}*)(cn=*{term2}* *{term1}* *{term3}*))" ,
"(|(&(givenName={term1})(middleName={initial2}*)(sn={term3}))(&(givenName={term2})(middleName={initial1}*)(sn={term3})))" ,
"(|(&(givenName={initial1}*)(middlename={initial2}*)(sn={term3}))(&(givenName={initial2}*)(middleName={initial1}*)(sn={term3})))" ,
"(sn={term3})" );
// create a pooled connection factory for searching
BlockingConnectionPool cp = new BlockingConnectionPool ( new DefaultConnectionFactory ( "ldap://directory.ldaptive.org" ));
cp . initialize ();
PooledConnectionFactory cf = new PooledConnectionFactory ( cp );
SearchTemplatesExecutor executor = new SearchTemplatesExecutor (
new AggregatePooledSearchExecutor (),
new PooledConnectionFactory [] { cf },
oneTermTemplate ,
twoTermTemplate ,
threeTermTemplate );
// get results for a one term query
Query oneTermQuery = new Query ( "fisher" );
SearchResult oneTermResult = executor . search ( oneTermQuery );
// get results for a two term query
Query twoTermQuery = new Query ( "daniel fisher" );
SearchResult twoTermResult = executor . search ( twoTermQuery );
// get results for a three term query
Query threeTermQuery = new Query ( "daniel william fisher" );
SearchResult threeTermResult = executor . search ( threeTermQuery );
Templates support is provided in a separate library that is available in the jars directory of the latest download .
Or included as a maven dependency:
<dependencies>
<dependency>
<groupId> org.ldaptive</groupId>
<artifactId> ldaptive-templates</artifactId>
<version> 1.2.4</version>
</dependency>
</dependencies>
Web Application
A common use case for this type of application is a web search engine. Ldaptive provides a web application which can be configured with custom search templates to expose results as LDIF, DSMLv1, or JSON. The configuration for this webapp leverages Spring IOC to configure the SearchTemplates
.
Sample Templates Context
Expects the ${ldapUrl} and ${baseDn} properties to be replaced at build time.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:p= "http://www.springframework.org/schema/p"
xmlns:util= "http://www.springframework.org/schema/util"
xsi:schemaLocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd" >
<bean id= "searchExecutor"
class= "org.ldaptive.concurrent.AggregatePooledSearchExecutor"
p:baseDn= "${baseDn}" >
<constructor-arg index= "0" >
<bean class= "java.util.concurrent.Executors"
factory-method= "newCachedThreadPool" />
</constructor-arg>
</bean>
<bean id= "connectionFactory"
class= "org.ldaptive.pool.PooledConnectionFactory" >
<constructor-arg index= "0" >
<bean class= "org.ldaptive.pool.BlockingConnectionPool"
init-method= "initialize"
p:blockWaitTime= "5000" >
<constructor-arg index= "0" >
<bean class= "org.ldaptive.pool.PoolConfig"
p:minPoolSize= "5"
p:maxPoolSize= "10"
p:validatePeriodically= "true"
/>
</constructor-arg>
<constructor-arg index= "1" >
<bean class= "org.ldaptive.DefaultConnectionFactory" >
<constructor-arg index= "0" >
<bean class= "org.ldaptive.ConnectionConfig"
p:ldapUrl= "${ldapUrl}"
/>
</constructor-arg>
</bean>
</constructor-arg>
<property name= "validator" >
<bean class= "org.ldaptive.pool.SearchValidator" />
</property>
</bean>
</constructor-arg>
</bean>
<!-- SEARCH CONFIG -->
<!-- searches are defined in the following section
A bean is defined to handle a specific number of query terms.
When a query arrives the searches are executed that match the number of
terms entered.
The following syntax is used to match query terms:
{term1} == the first query term entered
{term2} == the second query term entered, and so forth
{initial1} == the first letter of the first query term entered
in this manner you can construct search filters such as:
(givenName={term1})(middleName={initial2}*)(sn={term3}) -->
<!-- ONE TERM QUERIES -->
<bean id= "oneTermSearch" class= "org.ldaptive.templates.SearchTemplates" >
<constructor-arg>
<list>
<!-- phone number search -->
<!-- note that openldap removes dashes and spaces for all phone number queries -->
<value> (telephoneNumber={term1})</value>
<value> (telephoneNumber=*{term1})</value>
<!-- name search -->
<value> (|(givenName={term1})(sn={term1}))</value>
<value> (|(givenName={term1}*)(sn={term1}*))</value>
<value> (|(givenName=*{term1}*)(sn=*{term1}*))</value>
<!-- email search -->
<value> (mail={term1})</value>
<value> (mail={term1}*)</value>
<value> (mail=*{term1}*)</value>
</list>
</constructor-arg>
</bean>
<!-- TWO TERM QUERIES -->
<bean id= "twoTermSearch" class= "org.ldaptive.templates.SearchTemplates" >
<constructor-arg>
<list>
<!-- name search -->
<value> (& (givenName={term1})(sn={term2}))</value>
<value> (cn={term1} {term2})</value>
<value> (& (givenName={term1}*)(sn={term2}*))</value>
<value> (cn={term1}* {term2}*)</value>
<value> (& (givenName=*{term1}*)(sn=*{term2}*))</value>
<value> (cn=*{term1}* *{term2}*)</value>
<!-- initial search -->
<value> (|(& (givenName={initial1}*)(sn={term2}))(& (middleName={initial1}*)(sn={term2})))</value>
<!-- last name search -->
<value> (sn={term2})</value>
</list>
</constructor-arg>
</bean>
<!-- THREE TERM QUERIES -->
<bean id= "threeTermSearch" class= "org.ldaptive.templates.SearchTemplates" >
<constructor-arg>
<list>
<!-- name search -->
<value> (|(& (givenName={term1})(sn={term3}))(& (givenName={term2})(sn={term3})))</value>
<value> (|(cn={term1} {term2} {term3})(cn={term2} {term1} {term3}))</value>
<value> (|(& (givenName={term1}*)(sn={term3}*))(& (givenName={term2}*)(sn={term3}*)))</value>
<value> (|(cn={term1}* {term2}* {term3}*)(cn={term2}* {term1}* {term3}*))</value>
<value> (|(& (givenName=*{term1}*)(sn=*{term3}*))(& (givenName=*{term2}*)(sn=*{term3}*)))</value>
<value> (|(cn=*{term1}* *{term2}* *{term3}*)(cn=*{term2}* *{term1}* *{term3}*))</value>
<!-- initial search -->
<value> (|(& (givenName={term1})(middleName={initial2}*)(sn={term3}))(& (givenName={term2})(middleName={initial1}*)(sn={term3})))</value>
<value> (|(& p(givenName={initial1}*)(middlename={initial2}*)(sn={term3}))(& (givenName={initial2}*)(middleName={initial1}*)(sn={term3})))</value>
<!-- last name search -->
<value> (sn={term3})</value>
</list>
</constructor-arg>
</bean>
</beans>
Sample web.xml
Expects to find /templates-context.xml in the classpath and returns search results in JSON format. Ignores query terms of length 1 or 2, which typically aren’t indexed in LDAP.
<?xml version="1.0" encoding="UTF-8"?>
<web-app id= "ldaptive-templates"
version= "2.4"
xmlns= "http://java.sun.com/xml/ns/j2ee"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation= "http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" >
<display-name> Templates Search</display-name>
<description>
Templates Search is a web application which accepts search queries and returns best fit information from a LDAP.
</description>
<!-- Search servlet -->
<servlet>
<servlet-name> JsonSearch</servlet-name>
<servlet-class> org.ldaptive.servlets.SearchServlet</servlet-class>
<init-param>
<param-name> searchExecutorClass</param-name>
<param-value> org.ldaptive.servlets.JsonServletSearchTemplatesExecutor</param-value>
</init-param>
<!-- Classpath location of the spring context -->
<init-param>
<param-name> springContextPath</param-name>
<param-value> /templates-context.xml</param-value>
</init-param>
<!-- Ignore pattern -->
<init-param>
<param-name> ignorePattern</param-name>
<param-value> ^\w{1,2}$</param-value>
</init-param>
<load-on-startup> 1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name> JsonSearch</servlet-name>
<url-pattern> /Search</url-pattern>
</servlet-mapping>
</web-app>
This webapp can be used with the maven war overlay to customize its configuration:
<dependencies>
<dependency>
<groupId> org.ldaptive</groupId>
<artifactId> ldaptive-webapp</artifactId>
<version> 1.2.4</version>
</dependency>
</dependencies>