Archive

Archive for the ‘Getting Started’ Category

Getting Started with Spring Web Flow – Part 1

May 18th, 2012 4 comments

Spring Web Flow is a sub project of Spring that can be used to model user interface activities as flows. For example, consider the scenario of looking up an employee using an Employee directory application. This activity involves entering the employee’s name, filtering through the search results and viewing the details of the employee. Spring Web Flow makes it easy to define and implement such activities as flows.

In this post, I will discuss the details involved in creating a simple web flow application. This post will serve as a foundation to a much complex application we will see in a later post.

Spring Web Flow is an extension to Spring MVC. So, we start out by creating a Spring MVC web application. I have used my Spring MVC Maven Archetype to generate a basic MVC web application named “helloflow”. Here is the structure of the generated project:

MVC Project Structure

The web-Context.xml file has the standard Spring MVC bean declarations:


<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/jsp/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<context:component-scan base-package="com.inflinx.blog.helloflow.web" />
	
</beans:beans>

Similarly, the web.xml file contains the standard DispatcherServlet and ContextLoaderListener declarations:


<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:application-Context.xml</param-value>
	</context-param>
	
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<servlet>
		<servlet-name>helloflow</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/web-Context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
		
	<servlet-mapping>
		<servlet-name>helloflow</servlet-name>
		<url-pattern>*.html</url-pattern>
	</servlet-mapping>

</web-app>

In order to add Spring Web Flow capabilities to the application, we need to add Web Flow jars to the project. This is done by simply adding the following Maven dependency to pom.xml:


<dependency>
	<groupId>org.springframework.webflow</groupId>
   	<artifactId>spring-webflow</artifactId>
    <version>2.3.1.RELEASE</version>
</dependency> 

We will be using XML to define flows. Before we start defining flows, we need to let the Spring MVC application know about web flow so that flow requests and flow execution can be handled properly. To keep things more modular, we will declare these web flow realted beans in a separate webflow-Context.xml file under WEB-INF folder. Here is the context file with web flow namespace declarations:


<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:flow="http://www.springframework.org/schema/webflow-config" 
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
	http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.3.xsd">

		
</beans>	

The first web flow bean that needs to be declared is the flow-registry bean:

	<flow:flow-registry flow-builder-services="flowBuilderServices" id="flowRegistry" base-path="/WEB-INF/flows/">
	<flow:flow-location id="hello" path="hello-flow.xml" /> 
</flow:flow-registry>	

The flow-registry bean serves as the registry of flow definitions. In the above declaration the base-path property tells that all the flows will be under “WEB-INF/flows”. The flow-location element indicates the existence of a “hello-flow” with the id “hello”. The id is important, as we will be using it to launch a flow later.

The flow-registry bean simply loads all the flows. The actual execution of the flow is performed by the flow-executor. The flow-executor bean declaration is shown below:

	<flow:flow-executor id="flowExecutor" flow-registry="flowRegistry" />

The next set of beans are the FlowHandlerAdapter and FlowHandlerMapping. The FlowHandlerAdapter bean is responsible for dispatching requests to flows where as the FlowHandlerMapping is responsible for mapping a request path to a specific flow.


<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
	<property name="flowExecutor" ref="flowExecutor" />
</bean>

<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
	<property name="flowRegistry" ref="flowRegistry" />
	<property name="order" value="0" />
</bean>	

The final set of beans that needs to be declared are the MvcViewFactoryCreator and flow-builder-services. These beans are responsible resolving views. By default the MvcViewFactoryCreator will look for the views in the same folder as that of the flow definition.


<bean id="flowViewResolver" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator">
	<property name="viewResolvers" ref="internalResourceViewResolver"/>
</bean>

<flow:flow-builder-services id="flowBuilderServices" view-factory-creator="flowViewResolver" />

This concludes the webflow-Context.xml definition. Here is complete context file declaration:


<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:flow="http://www.springframework.org/schema/webflow-config" 
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
	http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.3.xsd">

	
	<!-- Registry of executable flows -->
	<flow:flow-registry flow-builder-services="flowBuilderServices" id="flowRegistry" base-path="/WEB-INF/flows/">
		<flow:flow-location id="hello" path="hello-flow.xml" /> 
	</flow:flow-registry>
	
	<!-- Executes flows -->
	<flow:flow-executor id="flowExecutor" flow-registry="flowRegistry" />	
	
	<!-- Dispatches requests to Flows -->
	<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
		<property name="flowExecutor" ref="flowExecutor" />
	</bean>
	
	<!-- Maps specific paths to flows. For example, /hello will look for a flow with id hello -->
	<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
		<property name="flowRegistry" ref="flowRegistry" />
		<property name="order" value="0" />
	</bean>
	
	<bean id="flowViewResolver" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator" />

	<flow:flow-builder-services id="flowBuilderServices" view-factory-creator="flowViewResolver" />
	
</beans>

The next step is to define a flow. Each flow is made up of states where things tend to happen and transitions that takes the flow from one state to another. We will create a hello flow that has a single state. To do that, simply create a new hello-flow.xml file under WEB-INF/flows folder. Here are the contents of the file:


<flow xmlns="http://www.springframework.org/schema/webflow"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
	
	<view-state id="hellowebflow" />
	
</flow>
	

As you notice, the above flow has only one state: a view state with id hellowebflow. View states displays user interface and invites the user to participate in the flow. So, when the above flow gets executed, Web Flow will look for a JSP page with the name hellowebflow.jsp under WEB-INF/flows and displays it. Here are the contents of the hellowebflow.jsp page:

<html>

	<head>
	
	</head>

	<body>
	
		<h2>Welcome to Webflow</h2>
	
	</body>

</html>

We are almost done with our application. We just need to make two more changes. The first change needs to be made in the web-Context.xml file to let Spring MVC know about the existence of webflow-Context.xml file. This is done by adding the following import element to web-Context.xml file:

<!-- Import the webflow definitions -->
<beans:import resource="webflow-Context.xml" />

Finally, we modify the web.xml file to instruct Spring to hanlde flow requests. This is done by adding a url-pattern element to the DispatcherServlet’s servlet-mapping element.


	<url-pattern>*.flow</url-pattern>
	

Once we have the application deployed, we can launch the flow using the URL: http::/helloflow/hello.flow
WebFlow App

Using Jackson for Java/JSON Conversion

May 10th, 2012 No comments

Jackson is one of the popular Java libraries that makes dealing with JSON easier. In this post, I will share techniques for converting JSON to Java and vice-versa.

The first thing we need to do is download the Jackson jar.

Maven users can simply include this dependency:

	<dependency>
		<groupId>org.codehaus.jackson</groupId>
  		<artifactId>jackson-jaxrs</artifactId>
  		<version>1.9.7</version>
	</dependency>

Converting Java objects to JSON

Consider a person object with fields first name, last name and ssn. The Java representation of the person is given below:

public class Person {
	
	private String firstName;
	private String lastName;
	private String ssn;
	
	public Person() {
		
	}
	 
	public Person(String firstName, String lastName, String ssn) {
		this.firstName = firstName;
		this.lastName = lastName;
		this.ssn = ssn;
	}
	
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getSsn() {
		return ssn;
	}
	public void setSsn(String ssn) {
		this.ssn = ssn;
	}
	
}

Converting a concrete instance of Person class using Jackson involves creating a new ObjectMapper and invoking the writeValueAsString method. The ObjectMapper instance is threadsafe and can be used across multiple threads. The code is shown below:

	public void convertJavaToJson()
	{
		ObjectMapper objectMapper = new ObjectMapper();
		
		Person person = new Person("John", "Doe", "123-45-6789");
		
		try
		{
			String json = objectMapper.writeValueAsString(person);
			System.out.println(json);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

Upon running the method, the following output is produced:

{“firstName”:”John”,”lastName”:”Doe”,”ssn”:”123-45-6789″}

Jackson automatically converts any nested objects to JSON. For example, lets say each person has an instance of Address class shown below:

public class Address {
	
	private String address1;
	private String city;
	
	public String getAddress1() {
		return address1;
	}
	public void setAddress1(String address1) {
		this.address1 = address1;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
}

The covertJavaToJson method below is converted to add an address and produce JSON:

public void convertJavaToJson()
	{
		ObjectMapper objectMapper = new ObjectMapper();
		
		Person person = new Person("John", "Doe", "123-45-6789");
		
		Address address = new Address();
		address.setAddress1("Main Street");
		address.setCity("Salt Lake City");
		person.setAddress(address);
		
		
		try
		{
			String json = objectMapper.writeValueAsString(person);
			System.out.println(json);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

Here is the resulting JSON:

{“firstName”:”John”,”lastName”:”Doe”,”ssn”:”123-45-6789″,”address”:{“address1″:”Main Street”,”city”:”Salt Lake City”}}

It is possible to customize the JSON produced by Jackson. Let’s say we would like to modify the attribute names in the generated JSON to fName and lName. This can be done by simply annotating the Person’s fields with @@JsonProperty. Here is the modified class:

public class Person {
	
	@JsonProperty("fName")
	private String firstName;
	
	@JsonProperty("lName")
	private String lastName;
	//Other properties and getters/setters	
}

Here is the generated JSON with new attribute names.

{“ssn”:”123-45-6789″,”address”:{“address1″:”Main Street”,”city”:”Salt Lake City”},”fName”:”John”,”lName”:”Doe”}

It is also possible to filter certain fields from being part of the produced JSON. This is done by annotating the field with @JsonIgnore annotation. In the code we below, we placed the annotation on the Address field:

public class Person {
	
	@JsonProperty("fName")
	private String firstName;
	
	@JsonProperty("lName")
	private String lastName;
	private String ssn;
	
	@JsonIgnore
	private Address address;
	
	// Getters and setters
}

Now, when we run the conversion, address will not be included:

{“ssn”:”123-45-6789″,”fName”:”John”,”lName”:”Doe”}

Converting JSON to Java Objects

Let’s say we want to convert the following JSON representation of a person to Person object.

{“firstName”:”Jane”,”lastName”:”Doe”,”ssn”:”456-78-9012″}

In order to perform the conversion, we create an instance of ObjectMapper and invoke the readValue method on it as shown below:

public void convertJSONtoJava()
	{
		String personJson = "{\"firstName\":\"Jane\",\"lastName\":\"Doe\",\"ssn\":\"456-78-9012\"}";
		
		ObjectMapper objectMapper = new ObjectMapper();
		try
		{
			Person person = objectMapper.readValue(personJson, Person.class);
			System.out.println(person);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

Often times, the Java controllers or Servlets recieve JSON from clients with additional information. For example, the JSON below has an additional action parameter:

{“action”:”modify”,”firstName”:”Jane”,”lastName”:”Doe”,”ssn”:”456-78-9012″}

In those cases, mapping JSON to a Java object will result in UnrecognizedPropertyException:

org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field “action” (Class Person), not marked as ignorable
at [Source: java.io.StringReader@16bdb503; line: 1, column: 12] (through reference chain: Person[“action”])
at org.codehaus.jackson.map.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:53)
at org.codehaus.jackson.map.deser.StdDeserializationContext.unknownFieldException(StdDeserializationContext.java:267)

These situations can be easily addressed by annotating the Person class with @JsonIgnoreProperties annotation. Here is modified class:

@JsonIgnoreProperties(ignoreUnknown=true)
public class Person {
	// Fields and getters/setters
}

Adding JQuery UI Slider

October 1st, 2009 No comments

Here are five easy steps to add slider functionality to your web applications:

Step 1: Go to JQuery UI site and create a custom download. To keep things simple, select “No Theme”.under theme section and deselect all but UI Core and Slider checkboxes. Hit Download and unzip the downloaded file.

Step 2: Copy the jquery-1.3.2.min.js and “jquery-ui-1.7.2.custom.min.js” files into your project. These files should be under the js folder. Include the js files in your html/jsp page.

  
	
	
      

Step 3: Add the following styles to the page:

		
	

I am using the slider bar and slider handle images from YUI

Step 4: Add a DIV for the slider in the HTML.

		

Step 5: Initialize the slider in the document ready function:

	
      

That’s it. You can find a demo of this example here.

Getting started with SNMP4J

August 30th, 2009 6 comments

The Simple Network Management Protocol (SNMP) is heavily used to manage devices on a network. SNMP4J is an open source SNMP implementation that allows Java programs to create, send and receive SNMP messages. In this post, I will show a simple example to read the raw idle cpu time from a server.

In order to use SNMP4J API, you need to have the following jars in the project classpath:

  • snmp-1.3.jar
  • SNMP4J.jar
  • log4j.jar

The first step is to create a new communication object to connect to the remote host.

InetAddress hostAddress = InetAddress.getByName("remote_host_name");
SNMPv1CommunicationInterface communication = new SNMPv1CommunicationInterface(0, hostAddress, "community_name", 151);

The next step is to query the Management Information Base for the raw idle cpu time.

SNMPVarBindList list = comm.getMIBEntry("1.3.6.1.4.1.2021.11.53.0"); 
Here 1.3.6.1.4.1.2021.11.53.0 is the OID for the raw idle CPU time. 

The final step is to retrieve the value from the returned value. Since we know that the returned list should have only one value, we look up the first item.

SNMPSequence sequence = (SNMPSequence) list.getSNMPObjectAt(0);
String idleCPUTime =  ((SNMPInteger)sequence.getSNMPObjectAt(1)).toString();

Putting it all together:

import java.net.InetAddress;
import snmp.SNMPInteger;
import snmp.SNMPSequence;
import snmp.SNMPVarBindList;
import snmp.SNMPv1CommunicationInterface;

public class SnmpLookup 
{
    public String getIdleCpuTime(String name, int port,String community)
    {
        String idleCPUTime = null;
        try
        {
            InetAddress hostAddress = InetAddress.getByName(name);
            SNMPv1CommunicationInterface comm = new SNMPv1CommunicationInterface(0, hostAddress, community, port);
            SNMPVarBindList list = comm.getMIBEntry("1.3.6.1.4.1.2021.11.53.0");
            SNMPSequence sequence = (SNMPSequence) list.getSNMPObjectAt(0);
            idleCPUTime =  ((SNMPInteger)sequence.getSNMPObjectAt(1)).toString();
        }
        catch(Exception e)
        {
           // Purposefully swalloning the exception
        }   	
        return idleCPUTime;
    }   
}
Categories: Getting Started, SNMP4J Tags:

Getting started with Lucene – Part 2

September 11th, 2008 No comments

In this post I will highlight some of Lucene’s search functionality. Refer to part one of this series for creating indexes using Lucene.

Searching in Lucene involves submitting a search query to the IndexSearcher class. The IndexSearcher executes this query against an index and returns search results (hits). Here is a prototype implementation:

public Hits searchIndex(Query qthrows Exception
  {
    IndexSearcher searcher = new IndexSearcher("c:/lucene/index");
    return searcher.search(q);
  }

The IndexSearcher constructor takes the path to the index it needs to search against. The IndexSearcher class is thread safe and Lucene API recommends opening and using one IndexSearcher for all searches.

The Query class is an abstract class that encapsulates a user input. The simplest way to generate a concrete query is to use the QueryParser class. The following code generates a query for all the employees whose first name is Judy:

QueryParser parser = new QueryParser("firstName"new SimpleAnalyzer());
    Query query = parser.parse("Judy");
    Hits hits = searchIndex(query);

The first parameter to the QueryParser is the field name in the document against which the query is being made. For better results, the analyzer passed as the second parameter should be of the same type that is used while creating indexes.

The Hits class encapsulates search results. Hits can be easily iterated over to get to the “interesting” stuff:

for(int i = 0; i < hits.length(); i++)
    {
      Document d = hits.doc(i);
      System.out.println(d.getField("firstName").stringValue());
    }

QueryParser does a good job at interpreting user entered search expressions. If developers find limitations using QueryParser, Lucene provides a nice API to programmatically generate and combine queries. Let’s say a user wants to find all the Employees with first name Judy and last name Test:

Query fnQuery = new TermQuery(new Term("firstName""Judy"));
    Query lnQuery = new TermQuery(new Term("lastName""Test"));
    BooleanQuery query = new BooleanQuery();
    query.add(fnQuery, BooleanClause.Occur.MUST);
    query.add(lnQuery, BooleanClause.Occur.MUST);
    // Notice we are not analyzing user entered input before executing search
    Hits hits = searchIndex(query);

By default the returned search results are ordered by decreasing relevance. This however can be easily changed using overloaded search methods in IndexSearcher. The following code sorts the results of the above query on first name field:

Sort sort = new Sort("firstName");
    Hits sortedHits = indexSearcher.search(query, sort);

An important thing to remember is that fields used for sorting must not be tokenized. Otherwise you will run into this exception: “there are more terms than documents in field “XXXXXX”, but it’s impossible to sort on tokenized fields”.

Categories: Getting Started Tags: ,

Getting started with Lucene – Part 1

September 8th, 2008 No comments

Apache Lucene is a popular open source text search engine that can be easily embedded in applications needing search functionality. Lucene is not a full blown, out of box web site search engine or crawler. Instead as you will soon see, Lucene exposes a small API to create and search indexes. In this first part of the series, I will show how Lucene can be used to create indexes.

Before any searches can be performed on large amounts of data, it is essential to convert the data into a easy to lookup format. This conversion process is called Indexing (much like a book index). Indexes created by Lucene contain a collection of documents and are usually stored as a list of files on the file system. A Lucene document itself is a sequence of name-value pairs called fields. The strings in a field are referred as terms.

Let say we are writing an employee search application that allows employees lookup each others information. The first step in the process is indexing the employee information. For the sake of simplicity, let’s assume that the employee information is available as a list of Employee objects. Here is the prototype method for creating a Lucene index (using the 2.3.2 version of Lucene API):

public void createIndex() throws Exception
  {
    // Create a writer
    IndexWriter writer = new IndexWriter("c:/lucene/index/"new SimpleAnalyzer());

    // Add documents to the index  
    addDocuments(writer);
    
    // Lucene recommends calling optimize upon completion of indexing
    writer.optimize();
    // clean up
    writer.close();  
  }

IndexWriter is the heart to Lucene indexing. It creates a new index and exposes API to add documents to the index. The first parameter to the constructor is the file system path where Lucene needs to store the index files. Before Lucene can index text, the text needs to be broken down in to tokens which is done via an Analyzer. Lucene out of box provides a variety of analyzers such as SimpleAnalyzer, StandardAnalyzer, StopAnalyzer etc. An anlyzer is specified as the second parameter to the writer constructor.

The next step in the process is adding the documents to the index. Here is a prototype implementation:

  public void addDocuments(IndexWriter writerthrows Exception
  {
    for(Employee e : employeeList)
    {
      // Create a document
      Document document = new Document();
      // Add fields to the document    
      document.add(new Field("firstName", e.getFirstName(), Field.Store.YES, Field.Index.TOKENIZED));
      document.add(new Field("lastName", e.getLastName(), Field.Store.YES, Field.Index.TOKENIZED));
      document.add(new Field("phoneNumber", e.getPhoneNumber(), Field.Store.YES, Field.Index.UN_TOKENIZED));    
    }
  }

In the above code, the first two parameters to the Field specify the field name, field value and the last two parameters provide metadata on how the field needs to be stored and indexed. When storing a field we have three options:
          Field.Store.YES – Original value is stored in the index
          Field.Store.COMPRESS – Original value is stored in the index in a compressed form
          Field.Store.NO – Field value is not stored in the index

Similarly, when indexing, we have couple options:
          Field.Index.NO – Field value is not indexed (useful for data like primary keys)
          Field.Index.TOKENIZED – Field value is analyzed and indexed (commonly used option)
          Field.Index.UN_TOKENIZED – Field value is not analyzed but indexed (useful for indexing “keywords” or data such as phone numbers)

Putting it all together:

import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.analysis.SimpleAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;

public class EmployeeIndexer {
  
  // Path to the index directory
  private static final String INDEX_DIRECTORY = "c:/lucene/index";
  
  private List<Employee> employeeList = new ArrayList<Employee>();
  
  public EmployeeIndexer() {
    employeeList.add(new Employee("Jane""Doe""123-456-8910"));
    employeeList.add(new Employee("John""Smith""123-456-8910"));
    employeeList.add(new Employee("Mike""Test""123-456-8910"));
    employeeList.add(new Employee("Judy""Test""123-456-8910"));
  }

  public void createIndex() throws Exception {
    // Create a writer
    IndexWriter writer = new IndexWriter(INDEX_DIRECTORY, new SimpleAnalyzer());

    // Add documents to the index  
    addDocuments(writer);
    
    // Lucene recommends calling optimize upon completion of indexing
    writer.optimize();
    // clean up
    writer.close();  
  }

  public void addDocuments(IndexWriter writerthrows Exception {
    for(Employee e : employeeList) {
      // Create a document
      Document document = new Document();
      // Add fields to the document    
      document.add(new Field("firstName", e.getFirstName(), Field.Store.YES, Field.Index.TOKENIZED));
      document.add(new Field("lastName", e.getLastName(), Field.Store.YES, Field.Index.TOKENIZED));
      document.add(new Field("phoneNumber", e.getPhoneNumber(), Field.Store.YES, Field.Index.UN_TOKENIZED));    
    }
  }
  
  public class Employee {
    private String firstName;
    private String lastName;
    private String phoneNumber;

    public Employee(String firstName, String lastName, String phoneNumber) {
      this.firstName = firstName;
      this.lastName = lastName;
      this.phoneNumber = phoneNumber;
    }
    
    public String getFirstName() {
      return firstName;
    }
    public void setFirstName(String firstName) {
      this.firstName = firstName;
    }
    public String getLastName() {
      return lastName;
    }
    public void setLastName(String lastName) {
      this.lastName = lastName;
    }
    public String getPhoneNumber() {
      return phoneNumber;
    }
    public void setPhoneNumber(String phoneNumber) {
      this.phoneNumber = phoneNumber;
    }
  }
}

Categories: Getting Started Tags: ,